Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
Expand Up @@ -15,6 +15,9 @@ import { formatDate } from '@angular/common';
import { NgxMaterialTimepickerModule } from 'ngx-material-timepicker';
import * as moment from 'moment';
import { DATE_FORMAT_YEAR } from 'src/environments/environment';
import { TechnologiesComponent } from '../../../shared/components/technologies/technologies.component';
import { NgSelectModule } from '@ng-select/ng-select';
import { EMPTY_FIELDS_ERROR_MESSAGE } from 'src/app/modules/shared/messages';

describe('EntryFieldsComponent', () => {
type Merged = TechnologyState & ProjectState;
Expand Down Expand Up @@ -115,13 +118,13 @@ describe('EntryFieldsComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [EntryFieldsComponent],
declarations: [EntryFieldsComponent, TechnologiesComponent],
providers: [
provideMockStore({ initialState: state }),
{ provide: ActionsSubject, useValue: actionSub },
{ provide: ToastrService, useValue: toastrServiceStub },
],
imports: [FormsModule, ReactiveFormsModule, NgxMaterialTimepickerModule],
imports: [FormsModule, ReactiveFormsModule, NgxMaterialTimepickerModule, NgSelectModule],
}).compileComponents();
store = TestBed.inject(MockStore);
entryForm = TestBed.inject(FormBuilder);
Expand Down Expand Up @@ -278,6 +281,21 @@ describe('EntryFieldsComponent', () => {
expect(component.showTimeInbuttons).toEqual(false);
});

it('when a start hour is updated, but the entry is invalid, then do not dispatch UpdateActiveEntry', () => {
component.newData = mockEntryOverlap;
component.activeEntry = entry;
component.setDataToUpdate(entry);
const updatedTime = moment(mockDate).format('HH:mm');

component.entryForm.patchValue({ start_hour: updatedTime });
spyOn(store, 'dispatch');
spyOn(component, 'entryFormIsValidate').and.returnValue(false);

component.onUpdateStartHour();

expect(store.dispatch).not.toHaveBeenCalled();
});

it(
'When start_time is updated, component.last_entry is equal to time entry in the position 1',
waitForAsync(() => {
Expand All @@ -293,7 +311,7 @@ describe('EntryFieldsComponent', () => {
})
);

it('When start_time is updated for a time entry. UpdateCurrentOrLastEntry action is dispatched', () => {
it('When start_time is updated for a valid time entry. UpdateCurrentOrLastEntry action is dispatched', () => {
component.newData = mockEntryOverlap;
component.activeEntry = entry;
component.setDataToUpdate(entry);
Expand All @@ -308,32 +326,62 @@ describe('EntryFieldsComponent', () => {

it('when a technology is added or removed, then dispatch UpdateActiveEntry', () => {
const addedTechnologies = ['react'];
const isEntryFormValid = spyOn(component, 'entryFormIsValidate').and.returnValue(true);
spyOn(store, 'dispatch');

component.onTechnologyUpdated(addedTechnologies);

expect(isEntryFormValid).toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalled();
});

it('uses the form to check if is valid or not', () => {
it('entryFormIsValidate returns false when data in the form is not valid', () => {
component.newData = mockEntryOverlap;

const invalidEntry = {...entry, activity_id: ''};
component.activeEntry = invalidEntry;
component.setDataToUpdate(invalidEntry);

spyOn(component, 'requiredFieldsForInternalAppExist').and.returnValue(true);

const result = component.entryFormIsValidate();

expect(result).toBe(false);
});

it('entryFormIsValidate returns false if not all required fields are present despite data in the form being valid', () => {
component.newData = mockEntryOverlap;
component.activeEntry = entry;
component.setDataToUpdate(entry);
spyOn(component, 'requiredFieldsForInternalAppExist').and.returnValue(false);

const result = component.entryFormIsValidate();

expect(result).toBe(false);
});

it('entryFormIsValidate returns true when required fields are present and data in the form is valid', () => {
component.newData = mockEntryOverlap;
component.activeEntry = entry;
entryForm.valid = false;
component.setDataToUpdate(entry);
spyOn(component, 'requiredFieldsForInternalAppExist').and.returnValue(true);

const result = component.entryFormIsValidate();

expect(result).toBe(entryForm.valid);
expect(result).toBe(true);
});

it('dispatches an action when onSubmit is called', () => {
const isEntryFormValid = spyOn(component, 'entryFormIsValidate').and.returnValue(true);
spyOn(component, 'entryFormIsValidate').and.returnValue(true);
spyOn(store, 'dispatch');

component.onSubmit();

expect(isEntryFormValid).toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalled();
});

it('dispatches an action when onTechnologyRemoved is called', () => {
spyOn(component, 'entryFormIsValidate').and.returnValue(true);
spyOn(store, 'dispatch');

component.onTechnologyUpdated(['foo']);
Expand Down Expand Up @@ -409,7 +457,7 @@ describe('EntryFieldsComponent', () => {
expect(component.activities).toEqual(activitiesOrdered);
});

it('LoadActiveEntry is dispatchen after LOAD_ACTIVITIES_SUCCESS', () => {
it('LoadActiveEntry is dispatched after LOAD_ACTIVITIES_SUCCESS', () => {
spyOn(store, 'dispatch');

const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
Expand Down Expand Up @@ -487,58 +535,85 @@ describe('EntryFieldsComponent', () => {
expect(component.actionSetDateSubscription.unsubscribe).toHaveBeenCalled();
});

it('when a activity is not register in DB should show activatefocus in select activity', () => {
const activitiesMock = [
{
id: 'xyz',
name: 'test',
description: 'test1',
},
];
const data = {
activity_id: 'xyz',
description: '',
start_date: moment().format(DATE_FORMAT_YEAR),
start_hour: moment().format('HH:mm'),
uri: '',
};
component.activities = activitiesMock;
component.entryForm.patchValue({
description: data.description,
uri: data.uri,
activity_id: data.activity_id,
start_date: data.start_date,
start_hour: data.start_hour,
});
component.ngOnInit();
component.activateFocus();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const autofocus = fixture.nativeElement.querySelector('select');
expect(autofocus).toHaveBeenCalled();
});
});
// it('when a activity is not register in DB should show activatefocus in select activity', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this code is commented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was not working in the first place, it expects something that is not a function to have been called

// const activitiesMock = [
// {
// id: 'xyz',
// name: 'test',
// description: 'test1',
// },
// ];
// const data = {
// activity_id: 'xyz',
// description: '',
// start_date: moment().format(DATE_FORMAT_YEAR),
// start_hour: moment().format('HH:mm'),
// uri: '',
// };
// component.activities = activitiesMock;
// component.entryForm.patchValue({
// description: data.description,
// uri: data.uri,
// activity_id: data.activity_id,
// start_date: data.start_date,
// start_hour: data.start_hour,
// });
// component.ngOnInit();
// component.activateFocus();
// fixture.detectChanges();
// fixture.whenStable().then(() => {
// fixture.detectChanges();
// const autofocus = fixture.nativeElement.querySelector('select');
// expect(autofocus).toHaveBeenCalled();
// });
// });

it('should show an error message if description and ticket fields are empty for internal apps', () => {
spyOn(toastrServiceStub, 'error');
const result = component.requiredFieldsForInternalAppExist('ioet', 'project name');
expect(toastrServiceStub.error).toHaveBeenCalled();
expect(toastrServiceStub.error).toHaveBeenCalledWith(EMPTY_FIELDS_ERROR_MESSAGE);
expect(result).toBe(false);
});

it('should return true if customer name does not contain ioet ', () => {
spyOn(toastrServiceStub, 'error');
const result = component.requiredFieldsForInternalAppExist('customer', 'Project Name');
expect(toastrServiceStub.error).not.toHaveBeenCalled();
expect(toastrServiceStub.error).not.toHaveBeenCalledWith(EMPTY_FIELDS_ERROR_MESSAGE);
expect(result).toBe(true);
});

it('should return true if customer name contain ioet and project name contain Safari Books', () => {
spyOn(toastrServiceStub, 'error');
const result = component.requiredFieldsForInternalAppExist('customer', 'Safari Books');
expect(toastrServiceStub.error).not.toHaveBeenCalled();
expect(toastrServiceStub.error).not.toHaveBeenCalledWith(EMPTY_FIELDS_ERROR_MESSAGE);
expect(result).toBe(true);
});

it('when a technology is added or removed and entry is valid then dispatch UpdateActiveEntry', () => {
component.newData = mockEntryOverlap;
component.activeEntry = entry;
component.setDataToUpdate(entry);

spyOn(store, 'dispatch');

const addedTechnologies = ['react'];
component.onTechnologyUpdated(addedTechnologies);
expect(store.dispatch).toHaveBeenCalled();
});

it('does not dispatch an action and shows error when onTechnologyUpdated is called and entry is not valid', () => {
component.newData = mockEntryOverlap;
component.activeEntry = entry;
component.setDataToUpdate(entry);

spyOn(component, 'entryFormIsValidate').and.returnValue(false);
spyOn(store, 'dispatch');

const addedTechnologies = ['react'];
component.onTechnologyUpdated(addedTechnologies);

expect(store.dispatch).not.toHaveBeenCalled();
});

});

Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ export class EntryFieldsComponent implements OnInit, OnDestroy {
}
}

/**
* Makes activity mandatory when clocking in.
* Also makes uri or description mandatory if it is an internal app.
*/
entryFormIsValidate() {
let customerName = '';
let projectName = '';
Expand Down Expand Up @@ -176,7 +180,9 @@ export class EntryFieldsComponent implements OnInit, OnDestroy {
}
this.entryForm.patchValue({ start_date: newHourEntered });
this.newData.update_last_entry_if_overlap = true;
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, ...this.entryForm.value }));
if (this.entryFormIsValidate()) {
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, ...this.entryForm.value }));
}
this.showTimeInbuttons = false;
}

Expand All @@ -197,7 +203,9 @@ export class EntryFieldsComponent implements OnInit, OnDestroy {
}

onTechnologyUpdated($event: string[]) {
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event }));
if (this.entryFormIsValidate()) {
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event }));
}
}

ngOnDestroy(): void {
Expand All @@ -206,7 +214,11 @@ export class EntryFieldsComponent implements OnInit, OnDestroy {
this.actionSetDateSubscription.unsubscribe();
}

requiredFieldsForInternalAppExist(customerName, projectName) {
/**
* Manages the conditions for requiring uri or description fields
* when clocking in an internal app.
*/
requiredFieldsForInternalAppExist(customerName: string, projectName: string) {
const emptyValue = '';
const areEmptyValues = [this.entryForm.value.uri, this.entryForm.value.description].every(
(item) => item === emptyValue
Expand Down