Modernize CI/CD pipeline with comprehensive automation (#25)
* Initial plan * Add comprehensive CI/CD workflows and tooling - Add PR validation workflow with lint, format, type check, test, and build steps - Improve Docker build workflow with caching, security scanning, and manual trigger - Add CodeQL security analysis workflow - Add Dependabot configuration for automated dependency updates - Configure ESLint with TypeScript support - Add npm scripts for linting and formatting - Format code with Prettier - Fix linting issues in source code Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> * Update README and fix build configuration - Add CI/CD documentation to README - Document code quality tools and scripts - Disable font optimization in production build to avoid network dependency - Add build cache configuration for GitHub Actions Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com>
This commit is contained in:
1 parent
fa71dee6a2
commit
f7bd101fb7
32 files changed
+935
-340
No files matched your search
@@ -1,11 +1,6 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { provideZonelessChangeDetection } from '@angular/core';
|
||||
import {
|
||||
CalendarService,
|
||||
DayType,
|
||||
SelectedDate,
|
||||
SelectedDates,
|
||||
} from './calendar-service';
|
||||
import { CalendarService, DayType, SelectedDate, SelectedDates } from './calendar-service';
|
||||
import { DateRange } from '@angular/material/datepicker';
|
||||
|
||||
describe('CalendarService', () => {
|
||||
@@ -111,47 +106,32 @@ describe('CalendarService', () => {
|
||||
describe('SelectedDate', () => {
|
||||
it('should check if date is selected', () => {
|
||||
const date = new Date(2024, 0, 1);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.CP,
|
||||
new DateRange<Date>(date, date),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||
expect(selectedDate.isSelected(date)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check if date is start', () => {
|
||||
const date = new Date(2024, 0, 1);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.CP,
|
||||
new DateRange<Date>(date, date),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||
expect(selectedDate.isStart(date)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check if date is end', () => {
|
||||
const date = new Date(2024, 0, 1);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.CP,
|
||||
new DateRange<Date>(date, date),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||
expect(selectedDate.isEnd(date)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return correct classes for date', () => {
|
||||
const date = new Date(2024, 0, 1);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.CP,
|
||||
new DateRange<Date>(date, date),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||
const classes = selectedDate.getClasses(date);
|
||||
expect(classes).toContain(DayType.CP);
|
||||
});
|
||||
|
||||
it('should return tooltip for selected date', () => {
|
||||
const date = new Date(2024, 0, 1);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.CP,
|
||||
new DateRange<Date>(date, date),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||
const tooltip = selectedDate.getTooltip(date);
|
||||
expect(tooltip).toBeTruthy();
|
||||
});
|
||||
@@ -159,19 +139,13 @@ describe('SelectedDate', () => {
|
||||
it('should calculate length in days', () => {
|
||||
const startDate = new Date(2024, 0, 1);
|
||||
const endDate = new Date(2024, 0, 3);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.CP,
|
||||
new DateRange<Date>(startDate, endDate),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(startDate, endDate));
|
||||
expect(selectedDate.getLengthInDays()).toBe(3);
|
||||
});
|
||||
|
||||
it('should not mark TODAY as selected', () => {
|
||||
const date = new Date(2024, 0, 1);
|
||||
const selectedDate = new SelectedDate(
|
||||
DayType.TODAY,
|
||||
new DateRange<Date>(date, date),
|
||||
);
|
||||
const selectedDate = new SelectedDate(DayType.TODAY, new DateRange<Date>(date, date));
|
||||
expect(selectedDate.isSelected(date)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -180,18 +154,14 @@ describe('SelectedDates', () => {
|
||||
it('should push selected dates', () => {
|
||||
const selectedDates = new SelectedDates();
|
||||
const date = new Date(2024, 0, 1);
|
||||
selectedDates.push(
|
||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
||||
);
|
||||
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||
expect(selectedDates._datesSelected.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should check if date is selected', () => {
|
||||
const selectedDates = new SelectedDates();
|
||||
const date = new Date(2024, 0, 1);
|
||||
selectedDates.push(
|
||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
||||
);
|
||||
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||
expect(selectedDates.isSelected(date)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -212,9 +182,7 @@ describe('SelectedDates', () => {
|
||||
it('should compute heuristics', () => {
|
||||
const selectedDates = new SelectedDates();
|
||||
const date = new Date(2024, 0, 1);
|
||||
selectedDates.push(
|
||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
||||
);
|
||||
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||
const heuristic = selectedDates.computeHeuristics();
|
||||
expect(typeof heuristic).toBe('number');
|
||||
});
|
||||
@@ -222,9 +190,7 @@ describe('SelectedDates', () => {
|
||||
it('should return correct classes', () => {
|
||||
const selectedDates = new SelectedDates();
|
||||
const date = new Date(2024, 0, 1);
|
||||
selectedDates.push(
|
||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
||||
);
|
||||
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||
const classes = selectedDates.getClasses(date);
|
||||
expect(classes).toContain(DayType.CP);
|
||||
});
|
||||
@@ -232,9 +198,7 @@ describe('SelectedDates', () => {
|
||||
it('should return tooltip', () => {
|
||||
const selectedDates = new SelectedDates();
|
||||
const date = new Date(2024, 0, 1);
|
||||
selectedDates.push(
|
||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
||||
);
|
||||
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||
const tooltip = selectedDates.getTooltip(date);
|
||||
expect(tooltip).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -28,8 +28,8 @@ export class CalendarService {
|
||||
|
||||
// Get selected dates from current date to the next year
|
||||
getSelectedDatesFromNow(): SelectedDates {
|
||||
const holidays = this.getHolidays();
|
||||
let selectedDates: SelectedDates = new SelectedDates();
|
||||
const _holidays = this.getHolidays();
|
||||
const selectedDates: SelectedDates = new SelectedDates();
|
||||
// Logic to populate selectedDates from now until the next year
|
||||
return selectedDates;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export class CalendarService {
|
||||
getSelectedDatesForYear(): SelectedDates {
|
||||
const holidays = this.getHolidays();
|
||||
const monthInAYear = this.monthsInAYearFromNow();
|
||||
let selectedDates: SelectedDates = new SelectedDates();
|
||||
const selectedDates: SelectedDates = new SelectedDates();
|
||||
|
||||
// Use DateCacheService to get today's date components
|
||||
const {
|
||||
@@ -519,7 +519,7 @@ export class SelectedDates implements SelectedDateInterface {
|
||||
const daysInRange = selectedDate.getLengthInDays();
|
||||
|
||||
// For each day in the range, add it to the appropriate month
|
||||
let currentDate = new Date(selectedDate.range.start);
|
||||
const currentDate = new Date(selectedDate.range.start);
|
||||
for (let i = 0; i < daysInRange; i++) {
|
||||
const monthKey = currentDate.getFullYear() * 12 + currentDate.getMonth();
|
||||
monthCounts.set(monthKey, (monthCounts.get(monthKey) ?? 0) + 1);
|
||||
@@ -719,7 +719,10 @@ export class SelectedDates implements SelectedDateInterface {
|
||||
}
|
||||
}
|
||||
|
||||
lookForVacation(vacType: DayType, calendarService: CalendarService): { apply: () => void; heuristic: number } {
|
||||
lookForVacation(
|
||||
vacType: DayType,
|
||||
calendarService: CalendarService,
|
||||
): { apply: () => void; heuristic: number } {
|
||||
const daysOfYear = calendarService.monthsInAYearFromNow();
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0);
|
||||
|
||||
@@ -3,40 +3,40 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
||||
import { ThemeService } from './theme-service';
|
||||
|
||||
describe('ThemeService', () => {
|
||||
let service: ThemeService;
|
||||
let service: ThemeService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
});
|
||||
service = TestBed.inject(ThemeService);
|
||||
});
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
});
|
||||
service = TestBed.inject(ThemeService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should initialize with system preference', () => {
|
||||
expect(service.isLightMode()).toBeDefined();
|
||||
});
|
||||
it('should initialize with system preference', () => {
|
||||
expect(service.isLightMode()).toBeDefined();
|
||||
});
|
||||
|
||||
it('should toggle theme', () => {
|
||||
const initialMode = service.isLightMode();
|
||||
service.toggleTheme();
|
||||
expect(service.isLightMode()).toBe(!initialMode);
|
||||
});
|
||||
it('should toggle theme', () => {
|
||||
const initialMode = service.isLightMode();
|
||||
service.toggleTheme();
|
||||
expect(service.isLightMode()).toBe(!initialMode);
|
||||
});
|
||||
|
||||
it('should force light mode when specified', () => {
|
||||
service.toggleTheme(true);
|
||||
expect(service.isLightMode()).toBe(true);
|
||||
expect(document.body.classList.contains('light-mode')).toBe(true);
|
||||
expect(document.body.classList.contains('dark-mode')).toBe(false);
|
||||
});
|
||||
it('should force light mode when specified', () => {
|
||||
service.toggleTheme(true);
|
||||
expect(service.isLightMode()).toBe(true);
|
||||
expect(document.body.classList.contains('light-mode')).toBe(true);
|
||||
expect(document.body.classList.contains('dark-mode')).toBe(false);
|
||||
});
|
||||
|
||||
it('should force dark mode when specified', () => {
|
||||
service.toggleTheme(false);
|
||||
expect(service.isLightMode()).toBe(false);
|
||||
expect(document.body.classList.contains('light-mode')).toBe(false);
|
||||
expect(document.body.classList.contains('dark-mode')).toBe(true);
|
||||
});
|
||||
it('should force dark mode when specified', () => {
|
||||
service.toggleTheme(false);
|
||||
expect(service.isLightMode()).toBe(false);
|
||||
expect(document.body.classList.contains('light-mode')).toBe(false);
|
||||
expect(document.body.classList.contains('dark-mode')).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable, signal, PLATFORM_ID, inject } from '@angular/core';
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { VacationsNumber } from './calendar-service';
|
||||
|
||||
@Injectable({
|
||||
|
||||
Reference in new issue
Block a user