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,4 +1,4 @@
import { LoadActiveEntry, EntryActionTypes } from './../../store/entry.actions';
import { LoadActiveEntry, EntryActionTypes, UpdateEntry } from './../../store/entry.actions';
import { ActivityManagementActionTypes } from './../../../activities-management/store/activity-management.actions';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {MockStore, provideMockStore} from '@ngrx/store/testing';
Expand Down Expand Up @@ -27,6 +27,11 @@ describe('EntryFieldsComponent', () => {
error: (message?: string, title?: string, override?: Partial<IndividualConfig>) => { },
warning: (message?: string, title?: string, override?: Partial<IndividualConfig>) => { }
};
const lastDate = moment().format('YYYY-MM-DD');
const startHourTest = moment().subtract(5, 'hours').format('HH:mm:ss');
const endHourTest = moment().subtract(3, 'hours').format('HH:mm:ss');
const lastStartHourEntryEntered = new Date(`${lastDate}T${startHourTest.trim()}`).toISOString();
const lastEndHourEntryEntered = new Date(`${lastDate}T${endHourTest.trim()}`).toISOString();

const state = {
projects: {
Expand Down Expand Up @@ -57,6 +62,28 @@ describe('EntryFieldsComponent', () => {
},
entryList: [],
message: '',
timeEntriesDataSource: { data: [
{
activity_id: 'xyz',
activity_name: 'abc',
id: 'id-15',
project_id: 'project-id-15',
description: 'description for an entry',
uri: 'abc',
start_date : moment().toISOString(),
end_date : moment().toISOString(),
},
{
activity_id: 'xyz',
activity_name: 'abc',
id: 'id-15',
project_id: 'project-id-15',
description: 'description for an entry',
uri: 'abc',
start_date : lastStartHourEntryEntered,
end_date : lastEndHourEntryEntered,
}
]}
},
};

Expand Down Expand Up @@ -134,6 +161,37 @@ describe('EntryFieldsComponent', () => {
expect(toastrServiceStub.error).toHaveBeenCalled();
});

it('Displays an error message when the active entry has start_time before the start_time of another entry', () => {
component.newData = entry;
component.activeEntry = entry ;
component.setDataToUpdate(entry);
spyOn(toastrServiceStub, 'error');

const hourInThePast = moment().subtract(6, 'hour').format('HH:mm:ss');
component.entryForm.patchValue({ start_hour : hourInThePast});
component.onUpdateStartHour();

expect(toastrServiceStub.error).toHaveBeenCalled();
});

it('should reset to current start_date when start_date has an error', () => {
component.newData = entry;
component.activeEntry = entry ;
component.setDataToUpdate(entry);

const updatedTime = moment().subtract(6, 'hours').format('HH:mm:ss');
component.entryForm.patchValue({ start_hour : updatedTime});

spyOn(component.entryForm, 'patchValue');
component.onUpdateStartHour();

expect(component.entryForm.patchValue).toHaveBeenCalledWith(
{
start_hour: component.newData.start_hour
}
);
});

it('If start hour is in the future, reset to initial start_date in form', () => {
component.newData = entry;
component.activeEntry = entry ;
Expand All @@ -155,12 +213,40 @@ describe('EntryFieldsComponent', () => {
it('when a start hour is updated, then dispatch UpdateActiveEntry', () => {
component.activeEntry = entry ;
component.setDataToUpdate(entry);
const updatedTime = moment().format('HH:mm:ss');
component.entryForm.patchValue({ start_hour : updatedTime});
spyOn(store, 'dispatch');

component.onUpdateStartHour();
expect(store.dispatch).toHaveBeenCalled();
});

it('When start_time is updated, component.last_entry is equal to time entry in the position 1', async(() => {
component.activeEntry = entry ;
component.setDataToUpdate(entry);
const updatedTime = moment().format('HH:mm:ss');

component.entryForm.patchValue({ start_hour : updatedTime});
component.onUpdateStartHour();

expect(component.lastEntry).toBe(state.entries.timeEntriesDataSource.data[1]);
}));

it('When start_time is updated for a time entry. UpdateEntry and UpdateEntryRuning actions are dispatched', () => {
component.activeEntry = entry ;
component.setDataToUpdate(entry);
const lastEntry = state.entries.timeEntriesDataSource.data[1];
const updatedTime = moment().subtract(4, 'hours').format('HH:mm:ss');
const lastStartHourEntryEnteredTest = new Date(`${lastDate}T${updatedTime.trim()}`).toISOString();
component.entryForm.patchValue({ start_hour : updatedTime});
spyOn(store, 'dispatch');

component.onUpdateStartHour();

expect(store.dispatch).toHaveBeenCalledTimes(2);
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to verify here the above-mentioned actions have been dispatched.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, it's tested

expect(store.dispatch).toHaveBeenCalledWith(new UpdateEntry({id: lastEntry.id, end_date: lastStartHourEntryEnteredTest}));
});

it('when a technology is added, then dispatch UpdateActiveEntry', () => {
const addedTechnologies = ['react'];
spyOn(store, 'dispatch');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { EntryActionTypes, LoadActiveEntry } from './../../store/entry.actions';
import { filter } from 'rxjs/operators';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store, ActionsSubject } from '@ngrx/store';

import { Store, ActionsSubject, select } from '@ngrx/store';
import { Activity, NewEntry } from '../../../shared/models';
import { ProjectState } from '../../../customer-management/components/projects/components/store/project.reducer';
import { TechnologyState } from '../../../shared/store/technology.reducers';
import { ActivityState, LoadActivities } from '../../../activities-management/store';

import * as entryActions from '../../store/entry.actions';

import { get } from 'lodash';
import * as moment from 'moment';
import { ToastrService } from 'ngx-toastr';
import { formatDate } from '@angular/common';
import { getTimeEntriesDataSource } from '../../store/entry.selectors';

type Merged = TechnologyState & ProjectState & ActivityState;

Expand All @@ -29,6 +29,7 @@ export class EntryFieldsComponent implements OnInit {
activities: Activity[] = [];
activeEntry;
newData;
lastEntry;

constructor(
private formBuilder: FormBuilder,
Expand All @@ -47,7 +48,7 @@ export class EntryFieldsComponent implements OnInit {

ngOnInit(): void {
this.store.dispatch(new LoadActivities());

this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1));
this.actionsSubject$
.pipe(filter((action: any) => action.type === ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS))
.subscribe((action) => {
Expand All @@ -57,11 +58,13 @@ export class EntryFieldsComponent implements OnInit {

this.actionsSubject$
.pipe(
filter((action: any) => (
action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS ||
action.type === EntryActionTypes.UPDATE_ENTRY_SUCCESS
))
).subscribe((action) => {
filter(
(action: any) =>
action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS ||
action.type === EntryActionTypes.UPDATE_ENTRY_SUCCESS
)
)
.subscribe((action) => {
if (!action.payload.end_date) {
this.store.dispatch(new LoadActiveEntry());
this.store.dispatch(new entryActions.LoadEntriesSummary());
Expand Down Expand Up @@ -118,6 +121,7 @@ export class EntryFieldsComponent implements OnInit {
}

onUpdateStartHour() {
this.getLastEntry();
const startDate = formatDate(this.activeEntry.start_date, 'yyyy-MM-dd', 'en');
const newHourEntered = new Date(`${startDate}T${this.entryForm.value.start_hour.trim()}`).toISOString();
const isEntryDateInTheFuture = moment(newHourEntered).isAfter(moment());
Expand All @@ -126,10 +130,34 @@ export class EntryFieldsComponent implements OnInit {
this.entryForm.patchValue({ start_hour: this.newData.start_hour });
return;
}

const lastEntryStartDate = get(this.lastEntry, 'start_date', moment().subtract(1, 'hours'));
const isEntryDateInLastStartDate = moment(newHourEntered).isSameOrBefore(lastEntryStartDate);
if (isEntryDateInLastStartDate) {
this.toastrService.error('There is another time entry registered in this time range');
this.entryForm.patchValue({ start_hour: this.newData.start_hour });
return;
}
this.entryForm.patchValue({ start_date: newHourEntered });
this.dispatchEntries(newHourEntered);
}

private dispatchEntries(newHourEntered) {
const lastEntryEndDate = get(this.lastEntry, 'end_date', moment().subtract(1, 'hours'));
const isInLastEntry = moment(newHourEntered).isBefore(lastEntryEndDate);
if (isInLastEntry) {
this.store.dispatch(new entryActions.UpdateEntry({ id: this.lastEntry.id, end_date: newHourEntered }));
}
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, ...this.entryForm.value }));
}

private getLastEntry() {
const lastEntry$ = this.store.pipe(select(getTimeEntriesDataSource));
lastEntry$.subscribe((entry) => {
this.lastEntry = entry.data[1];
});
}

onTechnologyAdded($event: string[]) {
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event }));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy {
} else {
this.store.dispatch(new entryActions.SwitchTimeEntry(this.activeEntry.id, selectedProject));
this.projectsForm.setValue( { project_id: `${customerName} - ${name}`, } );
this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1));
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/app/modules/users/store/user.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum UserActionTypes {
LOAD_USERS = '[User] LOAD_USERS',
LOAD_USERS_SUCCESS = '[User] LOAD_USERS_SUCCESS',
LOAD_USERS_FAIL = '[User] LOAD_USERS_FAIL',
DEFAULT_USER = '[USER] DEFAULT_USER',
}

export class LoadUsers implements Action {
Expand All @@ -21,4 +22,8 @@ export class LoadUsersFail implements Action {
constructor(public error: string) {}
}

export type UserActions = LoadUsers | LoadUsersSuccess | LoadUsersFail;
export class DefaultUser implements Action {
public readonly type = UserActionTypes.DEFAULT_USER;
}

export type UserActions = LoadUsers | LoadUsersSuccess | LoadUsersFail | DefaultUser;
6 changes: 6 additions & 0 deletions src/app/modules/users/store/user.reducer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ describe('userReducer', () => {
expect(state.isLoading).toEqual(false);
expect(state.data.length).toBe(0);
});

it('on Default, ', () => {
const action = new actions.DefaultUser();
const state = userReducer(initialState, action);
expect(state).toEqual(initialState);
});
});