Skip to content

Commit 4fee00e

Browse files
Copilotrenemadsen
andcommitted
Add unit tests and refactorings for AssignedSiteDialog and WorkdayEntityDialog components
Co-authored-by: renemadsen <[email protected]>
1 parent 555f053 commit 4fee00e

File tree

4 files changed

+659
-8
lines changed

4 files changed

+659
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { AssignedSiteDialogComponent } from './assigned-site-dialog.component';
3+
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
4+
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
5+
import { TimePlanningPnSettingsService } from '../../../../services';
6+
import { Store } from '@ngrx/store';
7+
import { of } from 'rxjs';
8+
9+
describe('AssignedSiteDialogComponent', () => {
10+
let component: AssignedSiteDialogComponent;
11+
let fixture: ComponentFixture<AssignedSiteDialogComponent>;
12+
let mockSettingsService: jasmine.SpyObj<TimePlanningPnSettingsService>;
13+
let mockStore: jasmine.SpyObj<Store>;
14+
15+
const mockAssignedSiteData = {
16+
id: 1,
17+
siteId: 1,
18+
siteName: 'Test Site',
19+
useGoogleSheetAsDefault: false,
20+
useOnlyPlanHours: false,
21+
autoBreakCalculationActive: false,
22+
allowPersonalTimeRegistration: true,
23+
allowEditOfRegistrations: true,
24+
usePunchClock: false,
25+
usePunchClockWithAllowRegisteringInHistory: false,
26+
allowAcceptOfPlannedHours: false,
27+
daysBackInTimeAllowedEditingEnabled: false,
28+
thirdShiftActive: false,
29+
fourthShiftActive: false,
30+
fifthShiftActive: false,
31+
resigned: false,
32+
resignedAtDate: new Date().toISOString(),
33+
mondayPlanHours: 0,
34+
tuesdayPlanHours: 0,
35+
wednesdayPlanHours: 0,
36+
thursdayPlanHours: 0,
37+
fridayPlanHours: 0,
38+
saturdayPlanHours: 0,
39+
sundayPlanHours: 0,
40+
mondayCalculatedHours: null,
41+
tuesdayCalculatedHours: null,
42+
wednesdayCalculatedHours: null,
43+
thursdayCalculatedHours: null,
44+
fridayCalculatedHours: null,
45+
saturdayCalculatedHours: null,
46+
sundayCalculatedHours: null,
47+
startMonday: 480, // 08:00
48+
endMonday: 1020, // 17:00
49+
breakMonday: 60, // 1 hour
50+
};
51+
52+
beforeEach(async () => {
53+
mockSettingsService = jasmine.createSpyObj('TimePlanningPnSettingsService', [
54+
'getGlobalAutoBreakCalculationSettings',
55+
'updateAssignedSite',
56+
'getAssignedSite'
57+
]);
58+
mockStore = jasmine.createSpyObj('Store', ['select']);
59+
60+
mockStore.select.and.returnValue(of(true));
61+
mockSettingsService.getGlobalAutoBreakCalculationSettings.and.returnValue(
62+
of({ success: true, model: {} as any })
63+
);
64+
65+
await TestBed.configureTestingModule({
66+
declarations: [AssignedSiteDialogComponent],
67+
imports: [ReactiveFormsModule],
68+
providers: [
69+
FormBuilder,
70+
{ provide: MAT_DIALOG_DATA, useValue: mockAssignedSiteData },
71+
{ provide: TimePlanningPnSettingsService, useValue: mockSettingsService },
72+
{ provide: Store, useValue: mockStore }
73+
]
74+
}).compileComponents();
75+
76+
fixture = TestBed.createComponent(AssignedSiteDialogComponent);
77+
component = fixture.componentInstance;
78+
});
79+
80+
it('should create', () => {
81+
expect(component).toBeTruthy();
82+
});
83+
84+
describe('Time Conversion Utilities', () => {
85+
describe('padZero', () => {
86+
it('should pad single digit numbers with zero', () => {
87+
expect(component.padZero(0)).toBe('00');
88+
expect(component.padZero(5)).toBe('05');
89+
expect(component.padZero(9)).toBe('09');
90+
});
91+
92+
it('should not pad double digit numbers', () => {
93+
expect(component.padZero(10)).toBe('10');
94+
expect(component.padZero(59)).toBe('59');
95+
expect(component.padZero(99)).toBe('99');
96+
});
97+
});
98+
99+
describe('getConvertedValue', () => {
100+
it('should convert minutes to time format HH:MM', () => {
101+
expect(component.getConvertedValue(0, 0)).toBe('');
102+
expect(component.getConvertedValue(60)).toBe('01:00');
103+
expect(component.getConvertedValue(90)).toBe('01:30');
104+
expect(component.getConvertedValue(480)).toBe('08:00'); // 8 hours
105+
expect(component.getConvertedValue(1020)).toBe('17:00'); // 17 hours
106+
});
107+
108+
it('should return empty string when minutes is 0 and compareMinutes is also 0', () => {
109+
expect(component.getConvertedValue(0, 0)).toBe('');
110+
});
111+
112+
it('should return 00:00 when both minutes and compareMinutes are null or undefined', () => {
113+
expect(component.getConvertedValue(0, null)).toBe('');
114+
expect(component.getConvertedValue(0, undefined)).toBe('');
115+
});
116+
117+
it('should handle minutes with remainders correctly', () => {
118+
expect(component.getConvertedValue(125)).toBe('02:05'); // 2 hours 5 minutes
119+
expect(component.getConvertedValue(517)).toBe('08:37'); // 8 hours 37 minutes
120+
});
121+
});
122+
});
123+
124+
describe('Shift Hours Calculation', () => {
125+
describe('calculateDayHours', () => {
126+
it('should calculate hours for a single shift without break', () => {
127+
// 8:00 to 17:00 = 9 hours
128+
const result = component.calculateDayHours(480, 1020, 0, 0, 0, 0);
129+
expect(result).toBe('9:0');
130+
});
131+
132+
it('should calculate hours for a single shift with break', () => {
133+
// 8:00 to 17:00 with 1 hour break = 8 hours
134+
const result = component.calculateDayHours(480, 1020, 60, 0, 0, 0);
135+
expect(result).toBe('8:0');
136+
});
137+
138+
it('should calculate hours for two shifts', () => {
139+
// First shift: 8:00 to 12:00 (4 hours)
140+
// Second shift: 13:00 to 17:00 (4 hours)
141+
// Total: 8 hours
142+
const result = component.calculateDayHours(480, 720, 0, 780, 1020, 0);
143+
expect(result).toBe('8:0');
144+
});
145+
146+
it('should handle breaks in both shifts', () => {
147+
// First shift: 8:00 to 12:00 - 30 min break = 3.5 hours
148+
// Second shift: 13:00 to 17:00 - 30 min break = 3.5 hours
149+
// Total: 7 hours
150+
const result = component.calculateDayHours(480, 720, 30, 780, 1020, 30);
151+
expect(result).toBe('7:0');
152+
});
153+
154+
it('should handle shifts with partial hours', () => {
155+
// 8:00 to 16:30 with 30 min break = 8 hours
156+
const result = component.calculateDayHours(480, 990, 30, 0, 0, 0);
157+
expect(result).toBe('8:0');
158+
});
159+
160+
it('should return 0:0 when no shifts are provided', () => {
161+
const result = component.calculateDayHours(0, 0, 0, 0, 0, 0);
162+
expect(result).toBe('0:0');
163+
});
164+
165+
it('should handle only second shift', () => {
166+
// Second shift only: 13:00 to 17:00 = 4 hours
167+
const result = component.calculateDayHours(0, 0, 0, 780, 1020, 0);
168+
expect(result).toBe('4:0');
169+
});
170+
});
171+
});
172+
173+
describe('Form Initialization', () => {
174+
it('should initialize form with correct structure', () => {
175+
component.ngOnInit();
176+
177+
expect(component.assignedSiteForm).toBeDefined();
178+
expect(component.assignedSiteForm.get('useGoogleSheetAsDefault')).toBeDefined();
179+
expect(component.assignedSiteForm.get('useOnlyPlanHours')).toBeDefined();
180+
expect(component.assignedSiteForm.get('planHours')).toBeDefined();
181+
expect(component.assignedSiteForm.get('firstShift')).toBeDefined();
182+
expect(component.assignedSiteForm.get('secondShift')).toBeDefined();
183+
});
184+
185+
it('should populate form with data values', () => {
186+
component.ngOnInit();
187+
188+
expect(component.assignedSiteForm.get('useGoogleSheetAsDefault')?.value).toBe(false);
189+
expect(component.assignedSiteForm.get('useOnlyPlanHours')?.value).toBe(false);
190+
});
191+
192+
it('should create shift forms for each day of the week', () => {
193+
component.ngOnInit();
194+
195+
const days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
196+
const firstShift = component.assignedSiteForm.get('firstShift');
197+
198+
days.forEach(day => {
199+
expect(firstShift?.get(day)).toBeDefined();
200+
expect(firstShift?.get(day)?.get('start')).toBeDefined();
201+
expect(firstShift?.get(day)?.get('end')).toBeDefined();
202+
expect(firstShift?.get(day)?.get('break')).toBeDefined();
203+
});
204+
});
205+
});
206+
207+
describe('Break Settings', () => {
208+
beforeEach(() => {
209+
component.ngOnInit();
210+
mockSettingsService.getGlobalAutoBreakCalculationSettings.and.returnValue(
211+
of({
212+
success: true,
213+
model: {
214+
mondayBreakMinutesDivider: 480,
215+
mondayBreakMinutesPrDivider: 30,
216+
mondayBreakMinutesUpperLimit: 60
217+
} as any
218+
})
219+
);
220+
});
221+
222+
it('should copy break settings from global settings for monday', () => {
223+
// Reinitialize to get the new global settings
224+
component.ngOnInit();
225+
226+
component.copyBreakSettings('monday');
227+
228+
const mondayBreak = component.assignedSiteForm.get('autoBreakSettings')?.get('monday');
229+
expect(mondayBreak?.get('breakMinutesDivider')?.value).toBe(480);
230+
expect(mondayBreak?.get('breakMinutesPrDivider')?.value).toBe(30);
231+
expect(mondayBreak?.get('breakMinutesUpperLimit')?.value).toBe(60);
232+
});
233+
234+
it('should handle missing global settings gracefully', () => {
235+
component['globalAutoBreakSettings'] = null;
236+
237+
component.copyBreakSettings('monday');
238+
239+
// Should not throw error and should not modify values
240+
const mondayBreak = component.assignedSiteForm.get('autoBreakSettings')?.get('monday');
241+
expect(mondayBreak).toBeDefined();
242+
});
243+
});
244+
245+
describe('Data Change Detection', () => {
246+
it('should detect when data has changed', () => {
247+
component.ngOnInit();
248+
249+
expect(component.hasDataChanged()).toBe(false);
250+
251+
component.data.useGoogleSheetAsDefault = true;
252+
253+
expect(component.hasDataChanged()).toBe(true);
254+
});
255+
});
256+
257+
describe('Time Field Update', () => {
258+
it('should set minutes correctly from time string', () => {
259+
component.ngOnInit();
260+
261+
component.setMinutes('08:30', 'startMonday');
262+
263+
expect(component.data['startMonday']).toBe(510); // 8*60 + 30
264+
});
265+
266+
it('should set minutes to 0 when empty value provided', () => {
267+
component.ngOnInit();
268+
component.data['startMonday'] = 480;
269+
270+
component.setMinutes('', 'startMonday');
271+
272+
expect(component.data['startMonday']).toBe(0);
273+
});
274+
275+
it('should handle different time formats', () => {
276+
component.ngOnInit();
277+
278+
component.setMinutes('12:00', 'startMonday');
279+
expect(component.data['startMonday']).toBe(720); // 12*60
280+
281+
component.setMinutes('00:30', 'endMonday');
282+
expect(component.data['endMonday']).toBe(30);
283+
});
284+
});
285+
});

eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,23 @@ export class AssignedSiteDialogComponent implements DoCheck, OnInit {
293293
end2NdShift: number,
294294
break2NdShift: number
295295
): string {
296-
let timeInMinutes = (end - start - breakTime) / 60;
297-
let timeInMinutes2NdShift = (end2NdShift - start2NdShift - break2NdShift) / 60;
298-
timeInMinutes += timeInMinutes2NdShift;
299-
const hours = Math.floor(timeInMinutes);
300-
const minutes = Math.round((timeInMinutes - hours) * 60);
296+
const firstShiftMinutes = this.calculateShiftMinutes(start, end, breakTime);
297+
const secondShiftMinutes = this.calculateShiftMinutes(start2NdShift, end2NdShift, break2NdShift);
298+
const totalMinutes = firstShiftMinutes + secondShiftMinutes;
299+
300+
return this.formatMinutesAsTime(totalMinutes);
301+
}
302+
303+
private calculateShiftMinutes(start: number, end: number, breakTime: number): number {
304+
if (!start || !end) {
305+
return 0;
306+
}
307+
return (end - start - (breakTime || 0)) / 60;
308+
}
309+
310+
private formatMinutesAsTime(totalMinutes: number): string {
311+
const hours = Math.floor(totalMinutes);
312+
const minutes = Math.round((totalMinutes - hours) * 60);
301313
return `${hours}:${minutes}`;
302314
}
303315

@@ -462,7 +474,7 @@ export class AssignedSiteDialogComponent implements DoCheck, OnInit {
462474
);
463475
}
464476

465-
private padZero(num: number): string {
477+
padZero(num: number): string {
466478
return num < 10 ? `0${num}` : `${num}`;
467479
}
468480

0 commit comments

Comments
 (0)