From 147f0f6009c03f5efd97d8e5a9710ef506171c81 Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Thu, 16 Jul 2026 15:35:41 +0200 Subject: [PATCH 1/2] Move the generic dialog out of the edit-survey feature folder into shared/, and rename JobDialogComponent to DialogComponent --- .../edit-details/edit-details.component.spec.ts | 8 ++++---- .../edit-details/edit-details.component.ts | 8 ++++---- .../edit-survey/edit-survey.component.spec.ts | 8 ++++---- .../edit-survey/edit-survey.component.ts | 8 ++++---- .../dialog/dialog.component.html} | 0 .../dialog/dialog.component.scss} | 0 .../dialog/dialog.component.spec.ts} | 16 ++++++++-------- .../dialog/dialog.component.ts} | 12 ++++++------ .../dialog/dialog.module.ts} | 8 ++++---- .../components/shared/header/header.component.ts | 6 +++--- .../loi-editor/loi-editor.component.spec.ts | 8 ++++---- .../shared/loi-editor/loi-editor.component.ts | 8 ++++---- .../survey-list/survey-list.component.spec.ts | 8 ++++---- .../shared/survey-list/survey-list.component.ts | 6 +++--- .../task-editor/task-form/task-form.component.ts | 6 +++--- web/src/app/guards/deactivate.guard.spec.ts | 2 +- web/src/app/guards/dirty-check.guard.ts | 6 +++--- 17 files changed, 59 insertions(+), 59 deletions(-) rename web/src/app/components/{edit-survey/job-dialog/job-dialog.component.html => shared/dialog/dialog.component.html} (100%) rename web/src/app/components/{edit-survey/job-dialog/job-dialog.component.scss => shared/dialog/dialog.component.scss} (100%) rename web/src/app/components/{edit-survey/job-dialog/job-dialog.component.spec.ts => shared/dialog/dialog.component.spec.ts} (90%) rename web/src/app/components/{edit-survey/job-dialog/job-dialog.component.ts => shared/dialog/dialog.component.ts} (95%) rename web/src/app/components/{edit-survey/job-dialog/job-dialog.module.ts => shared/dialog/dialog.module.ts} (85%) diff --git a/web/src/app/components/edit-survey/edit-details/edit-details.component.spec.ts b/web/src/app/components/edit-survey/edit-details/edit-details.component.spec.ts index 145cee127..e3187eaf3 100644 --- a/web/src/app/components/edit-survey/edit-details/edit-details.component.spec.ts +++ b/web/src/app/components/edit-survey/edit-details/edit-details.component.spec.ts @@ -39,8 +39,8 @@ import { SurveyService } from 'app/services/survey/survey.service'; import { DialogData, DialogType, - JobDialogComponent, -} from '../job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; describe('EditDetailsComponent', () => { let component: EditDetailsComponent; @@ -56,7 +56,7 @@ describe('EditDetailsComponent', () => { ); const newSurveyId = 'newSurveyId'; let dialogRefSpy: jasmine.SpyObj< - MatDialogRef + MatDialogRef >; let dialogSpy: jasmine.SpyObj; let navigationServiceSpy: jasmine.SpyObj; @@ -64,7 +64,7 @@ describe('EditDetailsComponent', () => { beforeEach(async () => { dialogRefSpy = jasmine.createSpyObj< - MatDialogRef + MatDialogRef >('MatDialogRef', ['afterClosed', 'close']); dialogSpy = jasmine.createSpyObj('MatDialog', ['open']); dialogSpy.open.and.returnValue(dialogRefSpy); diff --git a/web/src/app/components/edit-survey/edit-details/edit-details.component.ts b/web/src/app/components/edit-survey/edit-details/edit-details.component.ts index 1f4ce7ec2..36069c50a 100644 --- a/web/src/app/components/edit-survey/edit-details/edit-details.component.ts +++ b/web/src/app/components/edit-survey/edit-details/edit-details.component.ts @@ -27,8 +27,8 @@ import { SurveyService } from 'app/services/survey/survey.service'; import { DialogData, DialogType, - JobDialogComponent, -} from '../job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; interface DataSharingTermsDetails { description: string; @@ -87,7 +87,7 @@ export class EditDetailsComponent implements OnInit { openDeleteSurveyDialog() { this.dialog - .open(JobDialogComponent, { + .open(DialogComponent, { data: { dialogType: DialogType.DeleteSurvey }, panelClass: 'small-width-dialog', }) @@ -103,7 +103,7 @@ export class EditDetailsComponent implements OnInit { openCopySurveyDialog() { this.dialog - .open(JobDialogComponent, { + .open(DialogComponent, { data: { dialogType: DialogType.CopySurvey }, panelClass: 'small-width-dialog', }) diff --git a/web/src/app/components/edit-survey/edit-survey.component.spec.ts b/web/src/app/components/edit-survey/edit-survey.component.spec.ts index 32186d9d9..c67323990 100644 --- a/web/src/app/components/edit-survey/edit-survey.component.spec.ts +++ b/web/src/app/components/edit-survey/edit-survey.component.spec.ts @@ -39,8 +39,8 @@ import { SurveyService } from 'app/services/survey/survey.service'; import { DialogData, DialogType, - JobDialogComponent, -} from './job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; describe('EditSurveyComponent', () => { let fixture: ComponentFixture; @@ -52,7 +52,7 @@ describe('EditSurveyComponent', () => { let jobServiceSpy: jasmine.SpyObj; let dataStoreServiceSpy: jasmine.SpyObj; let dialogRefSpy: jasmine.SpyObj< - MatDialogRef + MatDialogRef >; let dialogSpy: jasmine.SpyObj; let initResolve: (value: void | PromiseLike) => void; @@ -144,7 +144,7 @@ describe('EditSurveyComponent', () => { dataStoreServiceSpy.tasks$.and.returnValue(new Subject>()); dialogRefSpy = jasmine.createSpyObj< - MatDialogRef + MatDialogRef >('MatDialogRef', ['afterClosed', 'close']); dialogSpy = jasmine.createSpyObj('MatDialog', ['open']); dialogSpy.open.and.returnValue(dialogRefSpy); diff --git a/web/src/app/components/edit-survey/edit-survey.component.ts b/web/src/app/components/edit-survey/edit-survey.component.ts index 0b5b11f61..5320ce490 100644 --- a/web/src/app/components/edit-survey/edit-survey.component.ts +++ b/web/src/app/components/edit-survey/edit-survey.component.ts @@ -35,8 +35,8 @@ import { Environment } from 'environments/environment-interface'; import { DialogData, DialogType, - JobDialogComponent, -} from './job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { toObservable, toSignal } from '@angular/core/rxjs-interop'; import { switchMap } from 'rxjs'; import { SurveyService } from 'app/services/survey/survey.service'; @@ -135,9 +135,9 @@ export class EditSurveyComponent { } openDialog(dialogType: DialogType, job: Job): void { - const dialogRef = this.dialog.open(JobDialogComponent, { + const dialogRef = this.dialog.open(DialogComponent, { autoFocus: [DialogType.AddJob, DialogType.RenameJob].includes(dialogType) - ? `#${JobDialogComponent.JOB_NAME_FIELD_ID}` + ? `#${DialogComponent.JOB_NAME_FIELD_ID}` : 'first-tabbable', data: { dialogType, jobName: job.name ?? '' }, panelClass: 'small-width-dialog', diff --git a/web/src/app/components/edit-survey/job-dialog/job-dialog.component.html b/web/src/app/components/shared/dialog/dialog.component.html similarity index 100% rename from web/src/app/components/edit-survey/job-dialog/job-dialog.component.html rename to web/src/app/components/shared/dialog/dialog.component.html diff --git a/web/src/app/components/edit-survey/job-dialog/job-dialog.component.scss b/web/src/app/components/shared/dialog/dialog.component.scss similarity index 100% rename from web/src/app/components/edit-survey/job-dialog/job-dialog.component.scss rename to web/src/app/components/shared/dialog/dialog.component.scss diff --git a/web/src/app/components/edit-survey/job-dialog/job-dialog.component.spec.ts b/web/src/app/components/shared/dialog/dialog.component.spec.ts similarity index 90% rename from web/src/app/components/edit-survey/job-dialog/job-dialog.component.spec.ts rename to web/src/app/components/shared/dialog/dialog.component.spec.ts index 536dfef8c..9a080b1ed 100644 --- a/web/src/app/components/edit-survey/job-dialog/job-dialog.component.spec.ts +++ b/web/src/app/components/shared/dialog/dialog.component.spec.ts @@ -23,16 +23,16 @@ import { import { By } from '@angular/platform-browser'; import { + DialogComponent, DialogData, DialogType, - JobDialogComponent, dialogConfigs, -} from './job-dialog.component'; +} from './dialog.component'; -describe('JobDialogComponent', () => { - let component: JobDialogComponent; - let fixture: ComponentFixture; - let dialogRefSpy: jasmine.SpyObj>; +describe('DialogComponent', () => { + let component: DialogComponent; + let fixture: ComponentFixture; + let dialogRefSpy: jasmine.SpyObj>; const mockDialogData: DialogData = { dialogType: DialogType.UndoJobs, @@ -42,7 +42,7 @@ describe('JobDialogComponent', () => { dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']); await TestBed.configureTestingModule({ - declarations: [JobDialogComponent], + declarations: [DialogComponent], imports: [MatDialogModule], providers: [ { provide: MatDialogRef, useValue: dialogRefSpy }, @@ -52,7 +52,7 @@ describe('JobDialogComponent', () => { }); beforeEach(() => { - fixture = TestBed.createComponent(JobDialogComponent); + fixture = TestBed.createComponent(DialogComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/web/src/app/components/edit-survey/job-dialog/job-dialog.component.ts b/web/src/app/components/shared/dialog/dialog.component.ts similarity index 95% rename from web/src/app/components/edit-survey/job-dialog/job-dialog.component.ts rename to web/src/app/components/shared/dialog/dialog.component.ts index 3e7f85837..999801103 100644 --- a/web/src/app/components/edit-survey/job-dialog/job-dialog.component.ts +++ b/web/src/app/components/shared/dialog/dialog.component.ts @@ -112,18 +112,18 @@ export interface DialogData { } @Component({ - selector: 'job-dialog', - templateUrl: './job-dialog.component.html', - styleUrls: ['./job-dialog.component.scss'], + selector: 'ground-dialog', + templateUrl: './dialog.component.html', + styleUrls: ['./dialog.component.scss'], standalone: false, }) -export class JobDialogComponent { +export class DialogComponent { public static readonly JOB_NAME_FIELD_ID = 'job-name'; readonly DialogType = DialogType; constructor( - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: DialogData ) {} @@ -148,6 +148,6 @@ export class JobDialogComponent { } get jobNameFieldId() { - return JobDialogComponent.JOB_NAME_FIELD_ID; + return DialogComponent.JOB_NAME_FIELD_ID; } } diff --git a/web/src/app/components/edit-survey/job-dialog/job-dialog.module.ts b/web/src/app/components/shared/dialog/dialog.module.ts similarity index 85% rename from web/src/app/components/edit-survey/job-dialog/job-dialog.module.ts rename to web/src/app/components/shared/dialog/dialog.module.ts index 1a05034ec..16438c656 100644 --- a/web/src/app/components/edit-survey/job-dialog/job-dialog.module.ts +++ b/web/src/app/components/shared/dialog/dialog.module.ts @@ -22,10 +22,10 @@ import { MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { JobDialogComponent } from 'app/components/edit-survey/job-dialog/job-dialog.component'; +import { DialogComponent } from 'app/components/shared/dialog/dialog.component'; @NgModule({ - declarations: [JobDialogComponent], + declarations: [DialogComponent], imports: [ CommonModule, MatDialogModule, @@ -34,6 +34,6 @@ import { JobDialogComponent } from 'app/components/edit-survey/job-dialog/job-di FormsModule, MatButtonModule, ], - exports: [JobDialogComponent], + exports: [DialogComponent], }) -export class JobDialogModule {} +export class DialogModule {} diff --git a/web/src/app/components/shared/header/header.component.ts b/web/src/app/components/shared/header/header.component.ts index 17fefdcd6..feea8ae46 100644 --- a/web/src/app/components/shared/header/header.component.ts +++ b/web/src/app/components/shared/header/header.component.ts @@ -20,8 +20,8 @@ import { Survey } from 'app/models/survey.model'; import { DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { AuthService } from 'app/services/auth/auth.service'; import { DraftSurveyService } from 'app/services/draft-survey/draft-survey.service'; import { NavigationService } from 'app/services/navigation/navigation.service'; @@ -114,7 +114,7 @@ export class HeaderComponent implements OnChanges { return; } - this.dialog.open(JobDialogComponent, { + this.dialog.open(DialogComponent, { data: { dialogType: DialogType.InvalidSurvey }, panelClass: 'small-width-dialog', }); diff --git a/web/src/app/components/shared/loi-editor/loi-editor.component.spec.ts b/web/src/app/components/shared/loi-editor/loi-editor.component.spec.ts index 5ff3632d9..a614bbb7c 100644 --- a/web/src/app/components/shared/loi-editor/loi-editor.component.spec.ts +++ b/web/src/app/components/shared/loi-editor/loi-editor.component.spec.ts @@ -25,8 +25,8 @@ import { of } from 'rxjs'; import { DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { ImportDialogComponent } from 'app/components/shared/import-dialog/import-dialog.component'; import { DataCollectionStrategy, Job } from 'app/models/job.model'; import { Coordinate } from 'app/models/geometry/coordinate'; @@ -202,7 +202,7 @@ describe('LoiEditorComponent', () => { .click(); expect(matDialogSpy.open).toHaveBeenCalledWith( - JobDialogComponent, + DialogComponent, jasmine.objectContaining({ data: jasmine.objectContaining({ dialogType: DialogType.DeleteLois, @@ -262,7 +262,7 @@ describe('LoiEditorComponent', () => { } as MatSlideToggleChange); expect(matDialogSpy.open).toHaveBeenCalledWith( - JobDialogComponent, + DialogComponent, jasmine.objectContaining({ data: jasmine.objectContaining({ dialogType: DialogType.DisableFreeForm, diff --git a/web/src/app/components/shared/loi-editor/loi-editor.component.ts b/web/src/app/components/shared/loi-editor/loi-editor.component.ts index 89b9b131f..ee990d8ff 100644 --- a/web/src/app/components/shared/loi-editor/loi-editor.component.ts +++ b/web/src/app/components/shared/loi-editor/loi-editor.component.ts @@ -22,8 +22,8 @@ import { List } from 'immutable'; import { DialogData, DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { DataCollectionStrategy, Job } from 'app/models/job.model'; import { LocationOfInterest } from 'app/models/loi.model'; import { Survey } from 'app/models/survey.model'; @@ -63,7 +63,7 @@ export class LoiEditorComponent { clearLois() { this.dialog - .open(JobDialogComponent, { + .open(DialogComponent, { data: { dialogType: DialogType.DeleteLois, surveyId: this.survey.id, @@ -87,7 +87,7 @@ export class LoiEditorComponent { toggleDataCollectorsCanAddLois(event: MatSlideToggleChange) { if (!event.checked) { this.dialog - .open(JobDialogComponent, { + .open(DialogComponent, { data: { dialogType: DialogType.DisableFreeForm, }, diff --git a/web/src/app/components/shared/survey-list/survey-list.component.spec.ts b/web/src/app/components/shared/survey-list/survey-list.component.spec.ts index 4c8e661fb..1ee622851 100644 --- a/web/src/app/components/shared/survey-list/survey-list.component.spec.ts +++ b/web/src/app/components/shared/survey-list/survey-list.component.spec.ts @@ -31,8 +31,8 @@ import { of } from 'rxjs'; import { DialogData, DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { AclEntry } from 'app/models/acl-entry.model'; import { Job } from 'app/models/job.model'; import { Role } from 'app/models/role.model'; @@ -62,7 +62,7 @@ describe('SurveyListComponent', () => { let authServiceSpy: jasmine.SpyObj; let navigationServiceSpy: jasmine.SpyObj; let dialogRefSpy: jasmine.SpyObj< - MatDialogRef + MatDialogRef >; let dialogSpy: jasmine.SpyObj; @@ -164,7 +164,7 @@ describe('SurveyListComponent', () => { ); dialogRefSpy = jasmine.createSpyObj< - MatDialogRef + MatDialogRef >('MatDialogRef', ['afterClosed', 'close']); dialogSpy = jasmine.createSpyObj('MatDialog', ['open']); dialogSpy.open.and.returnValue(dialogRefSpy); diff --git a/web/src/app/components/shared/survey-list/survey-list.component.ts b/web/src/app/components/shared/survey-list/survey-list.component.ts index a9f7e1141..dc5947136 100644 --- a/web/src/app/components/shared/survey-list/survey-list.component.ts +++ b/web/src/app/components/shared/survey-list/survey-list.component.ts @@ -20,8 +20,8 @@ import { List, Map } from 'immutable'; import { DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { Survey, SurveyGeneralAccess, @@ -121,7 +121,7 @@ export class SurveyListComponent { const isPasslisted = await this.authService.isPasslisted(); if (!isPasslisted) { - const dialogRef = this.dialog.open(JobDialogComponent, { + const dialogRef = this.dialog.open(DialogComponent, { data: { dialogType: DialogType.SurveyCreationDenied }, panelClass: 'small-width-dialog', }); diff --git a/web/src/app/components/shared/task-editor/task-form/task-form.component.ts b/web/src/app/components/shared/task-editor/task-form/task-form.component.ts index 291d6c94a..0d78ec143 100644 --- a/web/src/app/components/shared/task-editor/task-form/task-form.component.ts +++ b/web/src/app/components/shared/task-editor/task-form/task-form.component.ts @@ -38,8 +38,8 @@ import { List } from 'immutable'; import { DialogData, DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; import { Cardinality } from 'app/models/task/multiple-choice.model'; import { TaskConditionExpressionType, @@ -331,7 +331,7 @@ export class TaskFormComponent { else this.hasOtherOptionControl.setValue(false); } else { this.dialog - .open(JobDialogComponent, { + .open(DialogComponent, { data: { dialogType: DialogType.DeleteOption }, panelClass: 'small-width-dialog', }) diff --git a/web/src/app/guards/deactivate.guard.spec.ts b/web/src/app/guards/deactivate.guard.spec.ts index 63f3f6b82..a17b1c136 100644 --- a/web/src/app/guards/deactivate.guard.spec.ts +++ b/web/src/app/guards/deactivate.guard.spec.ts @@ -20,7 +20,7 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { dirtyCheckGuard } from './dirty-check.guard'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { of } from 'rxjs'; -import { DialogType } from 'app/components/edit-survey/job-dialog/job-dialog.component'; +import { DialogType } from 'app/components/shared/dialog/dialog.component'; describe('dirtyCheckGuard', () => { let draftSurveyServiceSpy: jasmine.SpyObj; diff --git a/web/src/app/guards/dirty-check.guard.ts b/web/src/app/guards/dirty-check.guard.ts index 51c3a9e57..d4da3c86b 100644 --- a/web/src/app/guards/dirty-check.guard.ts +++ b/web/src/app/guards/dirty-check.guard.ts @@ -22,8 +22,8 @@ import { DraftSurveyService } from 'app/services/draft-survey/draft-survey.servi import { DialogData, DialogType, - JobDialogComponent, -} from 'app/components/edit-survey/job-dialog/job-dialog.component'; + DialogComponent, +} from 'app/components/shared/dialog/dialog.component'; export const dirtyCheckGuard: CanDeactivateFn = async () => { const draftSurveyService = inject(DraftSurveyService); @@ -33,7 +33,7 @@ export const dirtyCheckGuard: CanDeactivateFn = async () => { return true; } - const dialogRef = dialog.open(JobDialogComponent, { + const dialogRef = dialog.open(DialogComponent, { data: { dialogType: DialogType.UndoJobs }, panelClass: 'small-width-dialog', }); From 948bce88fee68e2e86cd19692c94b27197b14bae Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Thu, 16 Jul 2026 15:54:09 +0200 Subject: [PATCH 2/2] added some tests --- .../shared/dialog/dialog.component.spec.ts | 179 ++++++++++++------ 1 file changed, 125 insertions(+), 54 deletions(-) diff --git a/web/src/app/components/shared/dialog/dialog.component.spec.ts b/web/src/app/components/shared/dialog/dialog.component.spec.ts index 9a080b1ed..5c5e9d797 100644 --- a/web/src/app/components/shared/dialog/dialog.component.spec.ts +++ b/web/src/app/components/shared/dialog/dialog.component.spec.ts @@ -15,12 +15,17 @@ */ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef, } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { DialogComponent, @@ -34,82 +39,148 @@ describe('DialogComponent', () => { let fixture: ComponentFixture; let dialogRefSpy: jasmine.SpyObj>; - const mockDialogData: DialogData = { - dialogType: DialogType.UndoJobs, - }; - - beforeEach(async () => { + async function setup(data: DialogData): Promise { dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']); await TestBed.configureTestingModule({ declarations: [DialogComponent], - imports: [MatDialogModule], + imports: [ + MatDialogModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + FormsModule, + NoopAnimationsModule, + ], providers: [ { provide: MatDialogRef, useValue: dialogRefSpy }, - { provide: MAT_DIALOG_DATA, useValue: mockDialogData }, + { provide: MAT_DIALOG_DATA, useValue: data }, ], }).compileComponents(); - }); - beforeEach(() => { fixture = TestBed.createComponent(DialogComponent); component = fixture.componentInstance; fixture.detectChanges(); - }); + } - it('should create', () => { - expect(component).toBeTruthy(); - }); + const buttons = () => + fixture.debugElement.queryAll(By.css('.mat-mdc-dialog-actions button')); + const backButton = () => buttons()[0]; + const continueButton = () => buttons()[buttons().length - 1]; + const titleText = () => + fixture.debugElement.query(By.css('.mat-mdc-dialog-title')).nativeElement + .textContent; - it('should close dialog when back button is clicked', () => { - const backButton = fixture.debugElement.query( - By.css('.mat-mdc-dialog-actions button:first-child') - ); - backButton.nativeElement.click(); - expect(dialogRefSpy.close).toHaveBeenCalled(); - }); + describe('confirmation dialog (UndoJobs)', () => { + beforeEach(async () => { + await setup({ dialogType: DialogType.UndoJobs }); + }); - it('should close dialog when continue button is clicked', () => { - const continueButton = fixture.debugElement.query( - By.css('.mat-mdc-dialog-actions button:last-child') - ); - continueButton.nativeElement.click(); - expect(dialogRefSpy.close).toHaveBeenCalled(); - }); + it('creates', () => { + expect(component).toBeTruthy(); + }); - it('should display the correct title in the template', () => { - const titleElement = fixture.debugElement.query( - By.css('.mat-mdc-dialog-title') - ); - expect(titleElement.nativeElement.textContent).toContain( - dialogConfigs[DialogType.UndoJobs].title - ); + it('renders the title, content, and both buttons', () => { + expect(titleText()).toContain('Unpublished changes'); + expect( + fixture.debugElement.query(By.css('.mat-mdc-dialog-content p')) + .nativeElement.textContent + ).toContain('If you leave this page'); + expect(buttons().length).toBe(2); + expect(backButton().nativeElement.textContent).toContain('Go back'); + expect(continueButton().nativeElement.textContent).toContain('Continue'); + }); + + it('does not render the job-name input', () => { + expect(fixture.debugElement.query(By.css('#job-name'))).toBeNull(); + }); + + it('closes without returning the dialog data when back is clicked', () => { + backButton().nativeElement.click(); + expect(dialogRefSpy.close).toHaveBeenCalled(); + expect(dialogRefSpy.close).not.toHaveBeenCalledWith(component.data); + }); + + it('closes with the dialog data when the continue button is clicked', () => { + continueButton().nativeElement.click(); + expect(dialogRefSpy.close).toHaveBeenCalledWith(component.data); + }); }); - it('should display the correct content in the template', () => { - const contentElement = fixture.debugElement.query( - By.css('.mat-mdc-dialog-content') - ); - expect(contentElement.nativeElement.textContent).toContain( - dialogConfigs[DialogType.UndoJobs].content - ); + describe('input dialog (AddJob)', () => { + beforeEach(async () => { + await setup({ dialogType: DialogType.AddJob, jobName: 'Initial' }); + }); + + it('renders the job-name field and its label, but no content', () => { + const input = fixture.debugElement.query(By.css('#job-name')); + expect(input).toBeTruthy(); + expect(input.nativeElement.id).toBe(DialogComponent.JOB_NAME_FIELD_ID); + expect( + fixture.debugElement.query(By.css('mat-label')).nativeElement.textContent + ).toContain('Job name'); + expect( + fixture.debugElement.query(By.css('.mat-mdc-dialog-content p')) + ).toBeNull(); + }); + + it('seeds the input from data.jobName', () => { + const input = fixture.debugElement.query(By.css('#job-name')) + .nativeElement as HTMLInputElement; + expect(input.value).toBe('Initial'); + }); + + it('writes edits back to data.jobName (ngModel)', async () => { + const input = fixture.debugElement.query(By.css('#job-name')) + .nativeElement as HTMLInputElement; + input.value = 'Trees'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + await fixture.whenStable(); + expect(component.data.jobName).toBe('Trees'); + }); + + it('renders only the AddJob buttons (Cancel/Create)', () => { + expect(buttons().length).toBe(2); + expect(backButton().nativeElement.textContent).toContain('Cancel'); + expect(continueButton().nativeElement.textContent).toContain('Create'); + }); }); - it('should display the back button with the correct label', () => { - const backButton = fixture.debugElement.query( - By.css('.mat-mdc-dialog-actions button:first-child') - ); - expect(backButton.nativeElement.textContent).toContain( - dialogConfigs[DialogType.UndoJobs].backButtonLabel - ); + describe('back-only dialog (InvalidSurvey)', () => { + beforeEach(async () => { + await setup({ dialogType: DialogType.InvalidSurvey }); + }); + + it('renders only the back button (no continue)', () => { + expect(buttons().length).toBe(1); + expect(backButton().nativeElement.textContent).toContain('Go back'); + }); + + it('closes without returning the dialog data when back is clicked', () => { + backButton().nativeElement.click(); + expect(dialogRefSpy.close).toHaveBeenCalled(); + expect(dialogRefSpy.close).not.toHaveBeenCalledWith(component.data); + }); }); - it('should display the continue button with the correct label', () => { - const continueButton = fixture.debugElement.query( - By.css('.mat-mdc-dialog-actions button:last-child') - ); - expect(continueButton.nativeElement.textContent).toContain( - dialogConfigs[DialogType.UndoJobs].continueButtonLabel + describe('every DialogType', () => { + const allTypes = Object.values(DialogType).filter( + (v): v is DialogType => typeof v === 'number' ); + + allTypes.forEach(type => { + it(`renders the configured title and buttons for ${DialogType[type]}`, async () => { + await setup({ dialogType: type }); + + const config = dialogConfigs[type]; + const expectedButtons = + (config.backButtonLabel ? 1 : 0) + + (config.continueButtonLabel ? 1 : 0); + + expect(titleText()).toContain(config.title); + expect(buttons().length).toBe(expectedButtons); + }); + }); }); });