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:
CopilotandTHoguet authored and GitHub committed 2025-11-03 21:09:09 +01:00
1 parent fa71dee6a2
commit f7bd101fb7
32 files changed
+935 -340

No files matched your search

@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
import { CalendarMonth } from './calendar-month';
describe('CalendarMonth', () => {
let component: CalendarMonth;
let fixture: ComponentFixture<CalendarMonth>;
let component: CalendarMonth;
let fixture: ComponentFixture<CalendarMonth>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarMonth],
providers: [provideZonelessChangeDetection()]
})
.compileComponents();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarMonth],
providers: [provideZonelessChangeDetection()],
}).compileComponents();
fixture = TestBed.createComponent(CalendarMonth);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CalendarMonth);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
import { CalendarSettings } from './calendar-settings';
describe('CalendarSettings', () => {
let component: CalendarSettings;
let fixture: ComponentFixture<CalendarSettings>;
let component: CalendarSettings;
let fixture: ComponentFixture<CalendarSettings>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarSettings],
providers: [provideZonelessChangeDetection()]
})
.compileComponents();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarSettings],
providers: [provideZonelessChangeDetection()],
}).compileComponents();
fixture = TestBed.createComponent(CalendarSettings);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CalendarSettings);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { MatButton, MatButtonModule } from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { MatIcon, MatIconModule } from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import { CalendarSettingsDialog } from '../dialog/calendar-settings-dialog/calendar-settings-dialog';
import { MatDividerModule } from '@angular/material/divider';
@@ -1,17 +1,21 @@
<div class="calendar-year">
@if (isLoading()) {
<div class="loading-overlay">
<div class="loading-spinner">
<mat-spinner></mat-spinner>
<p>Calcul en cours...</p>
<div class="loading-overlay">
<div class="loading-spinner">
<mat-spinner></mat-spinner>
<p>Calcul en cours...</p>
</div>
</div>
</div>
}
@for (month of months; track $index) {
<mat-card class="card">
<mat-card-content>
<app-calendar-month [monthIndex]="month" [year]="getYear(month)" [selectedDates]="selectedDates"></app-calendar-month>
</mat-card-content>
</mat-card>
<mat-card class="card">
<mat-card-content>
<app-calendar-month
[monthIndex]="month"
[year]="getYear(month)"
[selectedDates]="selectedDates"
></app-calendar-month>
</mat-card-content>
</mat-card>
}
</div>
@@ -66,5 +66,10 @@
}
:root {
@include mat.progress-spinner-overrides((size: 0.5rem, active-indicator-width: 0.25rem));
@include mat.progress-spinner-overrides(
(
size: 0.5rem,
active-indicator-width: 0.25rem,
)
);
}
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
import { CalendarYear } from './calendar-year';
describe('CalendarYear', () => {
let component: CalendarYear;
let fixture: ComponentFixture<CalendarYear>;
let component: CalendarYear;
let fixture: ComponentFixture<CalendarYear>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarYear],
providers: [provideZonelessChangeDetection()]
})
.compileComponents();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarYear],
providers: [provideZonelessChangeDetection()],
}).compileComponents();
fixture = TestBed.createComponent(CalendarYear);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CalendarYear);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -4,39 +4,38 @@ import { MatDialogRef } from '@angular/material/dialog';
import { CalendarSettingsDialog } from './calendar-settings-dialog';
describe('CalendarSettingsDialog', () => {
let component: CalendarSettingsDialog;
let fixture: ComponentFixture<CalendarSettingsDialog>;
let component: CalendarSettingsDialog;
let fixture: ComponentFixture<CalendarSettingsDialog>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarSettingsDialog],
providers: [
provideZonelessChangeDetection(),
{
provide: MatDialogRef,
useValue: {
close: jasmine.createSpy('close')
}
}
]
})
.compileComponents();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarSettingsDialog],
providers: [
provideZonelessChangeDetection(),
{
provide: MatDialogRef,
useValue: {
close: jasmine.createSpy('close'),
},
},
],
}).compileComponents();
fixture = TestBed.createComponent(CalendarSettingsDialog);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(CalendarSettingsDialog);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should have calendar settings service', () => {
expect(component['calendarSettingsService']).toBeDefined();
});
it('should have calendar settings service', () => {
expect(component['calendarSettingsService']).toBeDefined();
});
it('should close dialog when closeDialog is called', () => {
component.closeDialog();
expect(component.dialogRef.close).toHaveBeenCalled();
});
it('should close dialog when closeDialog is called', () => {
component.closeDialog();
expect(component.dialogRef.close).toHaveBeenCalled();
});
});
@@ -1,10 +1,8 @@
import { Component } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatDialogRef } from '@angular/material/dialog';
import { CalendarSettingsService } from '../../../services/calendar-settings-service';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { CdkFixedSizeVirtualScroll } from '@angular/cdk/scrolling';
@Component({
selector: 'app-calendar-settings-dialog',
@@ -3,39 +3,38 @@ import { provideZonelessChangeDetection } from '@angular/core';
import { UserInput } from './user-input';
describe('UserInput', () => {
let component: UserInput;
let fixture: ComponentFixture<UserInput>;
let component: UserInput;
let fixture: ComponentFixture<UserInput>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserInput],
providers: [provideZonelessChangeDetection()]
})
.compileComponents();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserInput],
providers: [provideZonelessChangeDetection()],
}).compileComponents();
fixture = TestBed.createComponent(UserInput);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(UserInput);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should initialize form with values from service', () => {
expect(component.userInputForm).toBeDefined();
expect(component.userInputForm.get('CP')).toBeDefined();
expect(component.userInputForm.get('RTT')).toBeDefined();
expect(component.userInputForm.get('Others')).toBeDefined();
});
it('should initialize form with values from service', () => {
expect(component.userInputForm).toBeDefined();
expect(component.userInputForm.get('CP')).toBeDefined();
expect(component.userInputForm.get('RTT')).toBeDefined();
expect(component.userInputForm.get('Others')).toBeDefined();
});
it('should have valid form controls', () => {
const cpControl = component.userInputForm.get('CP');
const rttControl = component.userInputForm.get('RTT');
const othersControl = component.userInputForm.get('Others');
it('should have valid form controls', () => {
const cpControl = component.userInputForm.get('CP');
const rttControl = component.userInputForm.get('RTT');
const othersControl = component.userInputForm.get('Others');
expect(cpControl).toBeTruthy();
expect(rttControl).toBeTruthy();
expect(othersControl).toBeTruthy();
});
expect(cpControl).toBeTruthy();
expect(rttControl).toBeTruthy();
expect(othersControl).toBeTruthy();
});
});
+1 -1
View File
@@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy, effect } from '@angular/core';
import { MatFormField, MatLabel } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { UserInputService } from '../../services/user-input-service';
import { VacationsNumber } from '../../services/calendar-service';
import { debounceTime, takeUntil, Subject } from 'rxjs';
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
import { UserSettings } from './user-settings';
describe('UserSettings', () => {
let component: UserSettings;
let fixture: ComponentFixture<UserSettings>;
let component: UserSettings;
let fixture: ComponentFixture<UserSettings>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserSettings],
providers: [provideZonelessChangeDetection()]
})
.compileComponents();
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserSettings],
providers: [provideZonelessChangeDetection()],
}).compileComponents();
fixture = TestBed.createComponent(UserSettings);
component = fixture.componentInstance;
fixture.detectChanges();
});
fixture = TestBed.createComponent(UserSettings);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});