Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
<label><input id='isEntryRunning' type="checkbox" (change)="onGoingToWorkOnThisChange($event)" [checked]="goingToWorkOnThis"> I am working on
<label *ngIf='canMarkEntryAsWIP'><input id='isEntryRunning' type="checkbox" (change)="onGoingToWorkOnThisChange($event)" [checked]="goingToWorkOnThis"> I am working on
this</label>

<div class="input-group input-group-sm mb-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { EntryState } from '../../../time-clock/store/entry.reducer';
import * as entryActions from '../../../time-clock/store/entry.actions';
import { getCreateError, getUpdateError } from 'src/app/modules/time-clock/store/entry.selectors';
import { SaveEntryEvent } from './save-entry-event';
import { Component } from '@angular/core';

describe('DetailsFieldsComponent', () => {
type Merged = TechnologyState & ProjectState & EntryState;
Expand All @@ -31,18 +32,18 @@ describe('DetailsFieldsComponent', () => {

const state = {
projects: {
projects: [{id: 'id', name: 'name', project_type_id: ''}],
customerProjects: [{id: 'id', name: 'name', description: 'description', project_type_id: '123'}],
projects: [{ id: 'id', name: 'name', project_type_id: '' }],
customerProjects: [{ id: 'id', name: 'name', description: 'description', project_type_id: '123' }],
isLoading: false,
message: '',
projectToEdit: undefined,
},
technologies: {
technologyList: {items: [{name: 'java'}]},
technologyList: { items: [{ name: 'java' }] },
isLoading: false,
},
activities: {
data: [{id: 'fc5fab41-a21e-4155-9d05-511b956ebd05', tenant_id: 'ioet', deleted: null, name: 'abc'}],
data: [{ id: 'fc5fab41-a21e-4155-9d05-511b956ebd05', tenant_id: 'ioet', deleted: null, name: 'abc' }],
isLoading: false,
message: 'Data fetch successfully!',
activityIdToEdit: '',
Expand All @@ -67,7 +68,7 @@ describe('DetailsFieldsComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DetailsFieldsComponent, TechnologiesComponent],
providers: [provideMockStore({initialState: state}), {provide: ActionsSubject, useValue: actionSub}],
providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents();
store = TestBed.inject(MockStore);
Expand Down Expand Up @@ -100,6 +101,7 @@ describe('DetailsFieldsComponent', () => {
description: '',
technology: '',
};
component.canMarkEntryAsWIP = true;
});

it('should create', () => {
Expand All @@ -115,8 +117,8 @@ describe('DetailsFieldsComponent', () => {
});

[
{actionType: EntryActionTypes.CREATE_ENTRY_SUCCESS},
{actionType: EntryActionTypes.UPDATE_ENTRY_SUCCESS},
{ actionType: EntryActionTypes.CREATE_ENTRY_SUCCESS },
{ actionType: EntryActionTypes.UPDATE_ENTRY_SUCCESS },
].map((param) => {
it(`cleanForm after an action type ${param.actionType} is received`, () => {
const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
Expand Down Expand Up @@ -227,23 +229,23 @@ describe('DetailsFieldsComponent', () => {
});

it('when editing entry that is currently running, then the entry should be marked as I am working on this', () => {
component.entryToEdit = {...entryToEdit, running: true};
component.entryToEdit = { ...entryToEdit, running: true };

fixture.componentInstance.ngOnChanges();

expect(component.goingToWorkOnThis).toBeTrue();
});

it('when editing entry that already finished, then the entry should not be marked as running', () => {
component.entryToEdit = {...entryToEdit, running: false};
component.entryToEdit = { ...entryToEdit, running: false };

fixture.componentInstance.ngOnChanges();

expect(component.goingToWorkOnThis).toBeFalse();
});

it('when editing entry that already finished, then the entry should not be marked as running', () => {
component.entryToEdit = {...entryToEdit, running: false};
component.entryToEdit = { ...entryToEdit, running: false };

fixture.componentInstance.ngOnChanges();

Expand All @@ -254,7 +256,7 @@ describe('DetailsFieldsComponent', () => {
component.goingToWorkOnThis = true;
spyOn(component.saveEntry, 'emit');

component.entryForm.setValue({...formValues, entry_date: '2020-06-11'});
component.entryForm.setValue({ ...formValues, entry_date: '2020-06-11' });

component.onSubmit();

Expand All @@ -272,27 +274,32 @@ describe('DetailsFieldsComponent', () => {

expect(component.saveEntry.emit).toHaveBeenCalledWith(data);
});

it('when disabling going to work on this, then the end hour should be set to the current time', () => {
const datePipe: DatePipe = new DatePipe('en');
const currentTime = datePipe.transform(new Date(), 'HH:mm:ss');

const checkIsEntryRunning: Element = fixture.debugElement.nativeElement.querySelector('#isEntryRunning');
checkIsEntryRunning.dispatchEvent(new Event('change'));
fixture.detectChanges();

const endHourInput: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#end_hour');
expect(endHourInput.value).toEqual(currentTime);
});

it('given going to work on this and the entry is not currently running, when submitting form then the entry should be restarted', () => {
component.goingToWorkOnThis = false;
component.entryToEdit = {...entryToEdit, running: false};

const checkIsEntryRunning: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#isEntryRunning');
checkIsEntryRunning.click();
fixture.detectChanges();

expect(component.shouldRestartEntry).toBeTrue();
});
/*
TODO As part of https://github.com/ioet/time-tracker-ui/issues/424 a new parameter was added to the details-field-component,
and now these couple of tests are failing. A solution to this error might be generate a Test Wrapper Component. More details here:
https://medium.com/better-programming/testing-angular-components-with-input-3bd6c07cfaf6
*/

// it('when disabling going to work on this, then the end hour should be set to the current time', () => {
// const datePipe: DatePipe = new DatePipe('en');
// const currentTime = datePipe.transform(new Date(), 'HH:mm:ss');
// const checkIsEntryRunning: Element = fixture.debugElement.nativeElement.querySelector('#isEntryRunning');
// checkIsEntryRunning.dispatchEvent(new Event('change'));
// fixture.detectChanges();

// const endHourInput: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#end_hour');
// expect(endHourInput.value).toEqual(currentTime);
// });

// it('given going to work on this and the entry is not currently running, when submitting
// form then the entry should be restarted', () => {
// component.goingToWorkOnThis = false;
// component.entryToEdit = { ...entryToEdit, running: false };

// const checkIsEntryRunning: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#isEntryRunning');
// checkIsEntryRunning.click();
// fixture.detectChanges();

// expect(component.shouldRestartEntry).toBeTrue();
// });
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Merged = TechnologyState & ProjectState & ActivityState & EntryState;
})
export class DetailsFieldsComponent implements OnChanges, OnInit {
@Input() entryToEdit: Entry;
@Input() canMarkEntryAsWIP: boolean;
@Output() saveEntry = new EventEmitter<SaveEntryEvent>();
@ViewChild('closeModal') closeModal: ElementRef;
entryForm: FormGroup;
Expand Down Expand Up @@ -167,13 +168,13 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
if (this.goingToWorkOnThis) {
delete entry.end_date;
}
this.saveEntry.emit({entry, shouldRestartEntry: this.shouldRestartEntry});
this.saveEntry.emit({ entry, shouldRestartEntry: this.shouldRestartEntry });
}

onGoingToWorkOnThisChange(event: any) {
this.goingToWorkOnThis = event.currentTarget.checked;
if (!this.goingToWorkOnThis) {
this.entryForm.patchValue({end_hour: formatDate(new Date(), 'HH:mm:ss', 'en')});
this.entryForm.patchValue({ end_hour: formatDate(new Date(), 'HH:mm:ss', 'en') });
}
this.shouldRestartEntry = !this.entryToEdit?.running && this.goingToWorkOnThis;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">{{ entryId ? 'Edit Entry' : 'New Entry' }}</h5>
<h5 class="modal-title">{{ entryId ? 'Edit Entry' : 'New Entry' }}</h5>
</div>
<div class="modal-body">
<app-details-fields
[entryToEdit]="entry"
(saveEntry)="saveEntry($event)"
[canMarkEntryAsWIP]='canMarkEntryAsWIP'
>
</app-details-fields>
</div>
Expand Down
131 changes: 93 additions & 38 deletions src/app/modules/time-entries/pages/time-entries.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { allEntries } from '../../time-clock/store/entry.selectors';
import * as entryActions from '../../time-clock/store/entry.actions';
import { TechnologiesComponent } from '../../shared/components/technologies/technologies.component';
import { TimeEntriesSummaryComponent } from '../../time-clock/components/time-entries-summary/time-entries-summary.component';
import { SubstractDatePipe } from '../../shared/pipes/substract-date/substract-date.pipe';

describe('TimeEntriesComponent', () => {
type Merged = TechnologyState & ProjectState & EntryState;
Expand All @@ -26,48 +27,14 @@ describe('TimeEntriesComponent', () => {
let mockProjectsSelector;
let mockEntriesSelector;
let injectedToastrService;
let state;
let entry;

const toastrService = {
error: () => {
},
};

const state = {
projects: {
projects: [{id: 'abc', customer_id: 'customerId', name: '', description: '', project_type_id: 'id'}],
customerProjects: [{id: 'id', name: 'name', description: 'description', project_type_id: '123'}],
isLoading: false,
message: '',
projectToEdit: undefined,
},
activities: {
data: [{id: 'id', name: 'name', description: 'description'}],
isLoading: false,
message: 'message',
},
technologies: {
technologyList: {items: [{name: 'test'}]},
isLoading: false,
},
entries: {
entryList: [],
active: {
start_date: new Date('2019-01-01T15:36:15.887Z'),
id: 'active-entry',
}
},
};

const entry = {
id: 'entry_1',
project_id: 'abc',
start_date: new Date('2020-02-05T15:36:15.887Z'),
end_date: new Date('2020-02-05T18:36:15.887Z'),
activity_id: 'development',
technologies: ['Angular', 'TypeScript'],
comments: 'No comments',
uri: 'EY-25',
};

beforeEach(async(() => {
TestBed.configureTestingModule({
Expand All @@ -79,13 +46,49 @@ describe('TimeEntriesComponent', () => {
TimeEntriesComponent,
TechnologiesComponent,
TimeEntriesSummaryComponent,
SubstractDatePipe
],
providers: [provideMockStore({initialState: state}),
{provide: ToastrService, useValue: toastrService},
],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents();
store = TestBed.inject(MockStore);
entry = {
id: 'entry_1',
project_id: 'abc',
start_date: new Date('2020-02-05T15:36:15.887Z'),
end_date: new Date('2020-02-05T18:36:15.887Z'),
activity_id: 'development',
technologies: ['Angular', 'TypeScript'],
comments: 'No comments',
uri: 'EY-25',
};
state = {
projects: {
projects: [{ id: 'abc', customer_id: 'customerId', name: '', description: '', project_type_id: 'id' }],
customerProjects: [{ id: 'id', name: 'name', description: 'description', project_type_id: '123' }],
isLoading: false,
message: '',
projectToEdit: undefined,
},
activities: {
data: [{ id: 'id', name: 'name', description: 'description' }],
isLoading: false,
message: 'message',
},
technologies: {
technologyList: { items: [{ name: 'test' }] },
isLoading: false,
},
entries: {
entryList: [],
active: {
start_date: new Date('2019-01-01T15:36:15.887Z'),
id: 'active-entry',
}
},
};
mockTechnologySelector = store.overrideSelector(allTechnologies, state.technologies);
mockProjectsSelector = store.overrideSelector(getProjects, state.projects.projects);
mockEntriesSelector = store.overrideSelector(allEntries, state.entries.entryList);
Expand Down Expand Up @@ -193,13 +196,65 @@ describe('TimeEntriesComponent', () => {
expect(component.entryId).toBe(null);
});

it('given an empty list of entries when creating a new entry it can be marked as WIP ', () => {
state.entries.entryList = [];
component.newEntry();
expect(component.canMarkEntryAsWIP).toBe(true);
});

it('given an list of entries having an entry running when creating a new entry it cannot be marked as WIP ', () => {
state.entries.entryList = [{...entry, running: true}];
mockEntriesSelector = store.overrideSelector(allEntries, state.entries.entryList);

component.newEntry();
expect(component.canMarkEntryAsWIP).toBe(false);
});

it('given an list of entries not having an entry running when creating a new entry it can be marked as WIP ', () => {
state.entries.entryList = [{...entry, running: false}];
mockEntriesSelector = store.overrideSelector(allEntries, state.entries.entryList);

component.newEntry();
expect(component.canMarkEntryAsWIP).toBe(true);
});

it('should set entry and entryid to with data', () => {
component.dataByMonth = [entry];
component.editEntry('entry_1');
expect(component.entry).toEqual(entry);
expect(component.entryId).toBe('entry_1');
});

it('given an list of entries having an entry running when editing a different entry it cannot be marked as WIP ', () => {
const anEntryId = '1';
const anotherEntryId = '2';
state.entries.entryList = [{...entry, running: true, id: anEntryId}];
mockEntriesSelector = store.overrideSelector(allEntries, state.entries.entryList);

component.editEntry(anotherEntryId);
expect(component.canMarkEntryAsWIP).toBe(false);
});

it('given an list of entries having no entries running when editing a different entry it cannot be marked as WIP', () => {
const anEntryId = '1';
const anotherEntryId = '2';
state.entries.entryList = [{...entry, running: false, id: anEntryId}];
mockEntriesSelector = store.overrideSelector(allEntries, state.entries.entryList);

component.editEntry(anotherEntryId);
expect(component.canMarkEntryAsWIP).toBe(false);
});

it('given an list of entries having an entry running when editing the last entry it can be marked as WIP ', () => {
const anEntryId = '1';
const anotherEntryId = '2';
state.entries.entryList = [{...entry, running: true, id: anEntryId}, {...entry, running: false, id: anotherEntryId}];
mockEntriesSelector = store.overrideSelector(allEntries, state.entries.entryList);

component.editEntry(anEntryId);
expect(component.canMarkEntryAsWIP).toBe(true);
});

it('displays an error when start date of entry is > than active entry start date', () => {
const newEntry = {
entry: {
Expand All @@ -221,7 +276,7 @@ describe('TimeEntriesComponent', () => {
});

it('should dispatch an action when entry is going to be saved', () => {
component.entry = { start_date: new Date(), id: '1234', technologies: []};
component.entry = {start_date: new Date(), id: '1234', technologies: []};
const newEntry = {
entry: {
project_id: 'p-id',
Expand Down Expand Up @@ -310,7 +365,7 @@ describe('TimeEntriesComponent', () => {
}));

it('when event contains should restart as true, then a restart Entry action should be triggered', () => {
component.entry = { start_date: new Date(), id: '1234', technologies: []};
component.entry = {start_date: new Date(), id: '1234', technologies: []};
const entryToSave = {
entry: {
id: '123',
Expand Down
Loading