|
| 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 | +}); |
0 commit comments