Skip to content

Commit 2fea6bf

Browse files
committed
Bug #15046: replace popup flow with single stepper dialog
1 parent 4f1f68d commit 2fea6bf

24 files changed

+1331
-761
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<vitamui-dialog-header
2+
[stepper]="stepper"
3+
[subhead]="'PROFILE.POP_UP_SAVE.SAVE_PROFILE.POPUP_CREATE_PROFILE_NOTICE_TITLE_DIALOG' | translate"
4+
[title]="[
5+
'PROFILE.POP_UP_CREATION.POPUP_CREATION_TITLE_DIALOG' | translate,
6+
'PROFILE.POP_UP_SAVE.SAVE_PROFILE.POPUP_CREATE_NOTICE_TITLE_DIALOG' | translate,
7+
]"
8+
></vitamui-dialog-header>
9+
10+
<form [formGroup]="noticeForm">
11+
<vitamui-common-stepper #stepper [(selectedIndex)]="stepIndex">
12+
<cdk-step>
13+
<mat-dialog-content>
14+
<div class="gap-5">
15+
<app-pastis-generic-popup
16+
[firstChoice]="ProfileType.PA"
17+
[secondChoice]="ProfileType.PUA"
18+
[title]="'PROFILE.POP_UP_CREATION.CHOICE.TITLE' | translate"
19+
(changeStatusEvent)="noticeForm.controls.profileType.setValue($event)"
20+
>
21+
<i
22+
class="vitamui-icon vitamui-icon-info vitamui-grey-700 clickable"
23+
[style.fontSize.px]="25"
24+
vitamuiTooltip="{{ 'PROFILE.CREATE_PROFILE.INFORMATION.TITRE_PA' | translate }} &#13; {{
25+
'PROFILE.CREATE_PROFILE.INFORMATION.CONTENT_PA' | translate
26+
}} &#13; {{ 'PROFILE.CREATE_PROFILE.INFORMATION.TITRE_PUA' | translate }} &#13; {{
27+
'PROFILE.CREATE_PROFILE.INFORMATION.CONTENT_PUA' | translate
28+
}}"
29+
vitamuiTooltipPosition="RIGHT"
30+
[vitamuiTooltipShowDelay]="300"
31+
></i>
32+
</app-pastis-generic-popup>
33+
<div>
34+
<div class="text bold caption primary">{{ 'PROFILE.CREATE_PROFILE.INFORMATION.SEDA_VERSION' | translate }}</div>
35+
<mat-radio-group class="hgap-2" formControlName="profileVersion">
36+
@for (profileVersionOption of ProfileVersionOptions; track profileVersionOption.version) {
37+
<mat-radio-button [value]="profileVersionOption.version">{{ profileVersionOption.label }}</mat-radio-button>
38+
}
39+
</mat-radio-group>
40+
</div>
41+
</div>
42+
</mat-dialog-content>
43+
<mat-dialog-actions>
44+
<vitamui-next-step [disabled]="!isStepOneValid()"></vitamui-next-step>
45+
<button (click)="onClose()" class="btn cancel link" type="button">
46+
{{ 'COMMON.UNDO' | translate }}
47+
</button>
48+
</mat-dialog-actions>
49+
</cdk-step>
50+
<cdk-step>
51+
<mat-dialog-content>
52+
<mat-dialog-content class="gap-3 align-items-stretch">
53+
<div class="row">
54+
@if (externalIdentifierEnabledSignal()) {
55+
<div class="col-10 form-control">
56+
<vitamui-common-input
57+
class="col-9 px-0"
58+
placeholder="{{ 'PROFILE.POP_UP_CREATION_NOTICE.IDENTIFIER' | translate }}"
59+
formControlName="identifier"
60+
>
61+
</vitamui-common-input>
62+
<vitamui-common-input-error
63+
*ngIf="noticeForm?.get('identifier')?.touched && noticeForm?.get('identifier')?.hasError('identifierExist')"
64+
>
65+
{{ 'ERROR_IDENTIFIER.IDENTIFIER_ALREADY_EXISTS' | translate }}
66+
</vitamui-common-input-error>
67+
</div>
68+
}
69+
</div>
70+
71+
<div class="row">
72+
<div class="col-10 form-control">
73+
<vitamui-common-input
74+
class="col-9 px-0"
75+
formControlName="name"
76+
placeholder="{{ 'PROFILE.POP_UP_CREATION_NOTICE.NAME' | translate }} "
77+
>
78+
</vitamui-common-input>
79+
</div>
80+
</div>
81+
82+
<div class="row">
83+
<div class="col-10 form-control">
84+
<vitamui-common-textarea
85+
formControlName="description"
86+
placeholder="{{ 'PROFILE.POP_UP_CREATION_NOTICE.DESCRIPTION' | translate }} "
87+
[rows]="4"
88+
style="width: 100%"
89+
>
90+
</vitamui-common-textarea>
91+
</div>
92+
</div>
93+
@if (modePUA()) {
94+
<div class="row">
95+
<div class="col-10 form-control">
96+
<div class="d-flex justify-content-between align-items-center py-1 px-2 mb-2">
97+
<vitamui-common-slide-toggle formControlName="additionalProperties">
98+
{{ 'PROFILE.POP_UP_CREATION_NOTICE.ALLOW_METADATA' | translate }}
99+
</vitamui-common-slide-toggle>
100+
</div>
101+
</div>
102+
</div>
103+
}
104+
<vitamui-select
105+
class="d-block"
106+
formControlName="status"
107+
[placeholder]="'PROFILE.POP_UP_CREATION_NOTICE.SAVE_AS_ACTIVE_OR_INACTIVE' | translate"
108+
[options]="statusOptions"
109+
[enableSearch]="false"
110+
></vitamui-select>
111+
</mat-dialog-content>
112+
</mat-dialog-content>
113+
<mat-dialog-actions>
114+
<div>
115+
<button type="submit" (click)="validate()" [disabled]="!isFormValid()" class="btn primary">
116+
{{ 'PROFILE.POP_UP_SAVE.SAVE_PROFILE.POPUP_CREATE_NOTICE_OK_LABEL' | translate }}
117+
</button>
118+
<button (click)="onClose()" class="btn cancel link" type="button">
119+
{{ 'COMMON.UNDO' | translate }}
120+
</button>
121+
</div>
122+
<vitamui-previous-step></vitamui-previous-step>
123+
</mat-dialog-actions>
124+
</cdk-step>
125+
</vitamui-common-stepper>
126+
</form>
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2022)
3+
* and the signatories of the "VITAM - Accord du Contributeur" agreement.
4+
*
5+
* contact@programmevitam.fr
6+
*
7+
* This software is a computer program whose purpose is to implement
8+
* implement a digital archiving front-office system for the secure and
9+
* efficient high volumetry VITAM solution.
10+
*
11+
* This software is governed by the CeCILL-C license under French law and
12+
* abiding by the rules of distribution of free software. You can use,
13+
* modify and/ or redistribute the software under the terms of the CeCILL-C
14+
* license as circulated by CEA, CNRS and INRIA at the following URL
15+
* "http://www.cecill.info".
16+
*
17+
* As a counterpart to the access to the source code and rights to copy,
18+
* modify and redistribute granted by the license, users are provided only
19+
* with a limited warranty and the software's author, the holder of the
20+
* economic rights, and the successive licensors have only limited
21+
* liability.
22+
*
23+
* In this respect, the user's attention is drawn to the risks associated
24+
* with loading, using, modifying and/or developing or reproducing the
25+
* software by the user in light of its specific status of free software,
26+
* that may mean that it is complicated to manipulate, and that also
27+
* therefore means that it is reserved for developers and experienced
28+
* professionals having in-depth computer knowledge. Users are therefore
29+
* encouraged to load and test the software's suitability as regards their
30+
* requirements in conditions enabling the security of their systems and/or
31+
* data to be ensured and, more generally, to use and operate it in the
32+
* same conditions as regards security.
33+
*
34+
* The fact that you are presently reading this means that you have had
35+
* knowledge of the CeCILL-C license and that you accept its terms.
36+
*/
37+
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
38+
39+
import { CreateProfilNoticeComponent } from './create-profil-notice.component';
40+
import { MatDialogRef } from '@angular/material/dialog';
41+
import { ProfileService } from '../../core/services/profile.service';
42+
import { ApplicationService, VitamUICommonModule, VitamUILibraryModule } from 'vitamui-library';
43+
import { TranslateModule, TranslateService } from '@ngx-translate/core';
44+
import { BehaviorSubject, of } from 'rxjs';
45+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
46+
import { ProfileType } from '../../models/profile-type.enum';
47+
import { MatRadioModule } from '@angular/material/radio';
48+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
49+
50+
describe('CreateProfilNoticeComponent', () => {
51+
let component: CreateProfilNoticeComponent;
52+
let fixture: ComponentFixture<CreateProfilNoticeComponent>;
53+
let mockDialogRef: jasmine.SpyObj<MatDialogRef<CreateProfilNoticeComponent>>;
54+
let mockProfileService: jasmine.SpyObj<ProfileService>;
55+
let mockAppService: jasmine.SpyObj<ApplicationService>;
56+
let mockTranslate: jasmine.SpyObj<TranslateService>;
57+
let externalIdSubject: BehaviorSubject<boolean>;
58+
59+
beforeEach(() => {
60+
mockDialogRef = jasmine.createSpyObj('MatDialogRef', ['close']);
61+
mockProfileService = jasmine.createSpyObj('ProfileService', ['checkPuaProfile', 'checkPaProfile', 'controlSchema']);
62+
mockAppService = jasmine.createSpyObj('ApplicationService', ['isApplicationExternalIdentifierEnabled']);
63+
mockProfileService.controlSchema = new BehaviorSubject<string>(JSON.stringify({ additionalProperties: true }));
64+
mockTranslate = jasmine.createSpyObj('TranslateService', ['instant']);
65+
66+
mockTranslate.instant.and.callFake((key: string) => key);
67+
externalIdSubject = new BehaviorSubject<boolean>(true);
68+
mockAppService.isApplicationExternalIdentifierEnabled.and.returnValue(externalIdSubject.asObservable());
69+
70+
TestBed.configureTestingModule({
71+
imports: [
72+
ReactiveFormsModule,
73+
TranslateModule.forRoot(),
74+
MatRadioModule,
75+
NoopAnimationsModule,
76+
CreateProfilNoticeComponent,
77+
VitamUICommonModule,
78+
VitamUILibraryModule,
79+
],
80+
providers: [
81+
FormBuilder,
82+
{ provide: MatDialogRef, useValue: mockDialogRef },
83+
{ provide: ProfileService, useValue: mockProfileService },
84+
{ provide: ApplicationService, useValue: mockAppService },
85+
],
86+
}).compileComponents();
87+
88+
fixture = TestBed.createComponent(CreateProfilNoticeComponent);
89+
component = fixture.componentInstance;
90+
});
91+
92+
it('should validate identifier via checkPaProfile when not PUA', fakeAsync(() => {
93+
component.profileTypeSignal.set(ProfileType.PA);
94+
fixture.detectChanges();
95+
mockProfileService.checkPaProfile.and.returnValue(of(true));
96+
component.noticeForm.controls.identifier?.setValue('DUPLICATE');
97+
98+
expect(mockProfileService.checkPaProfile).toHaveBeenCalledWith(jasmine.objectContaining({ identifier: 'DUPLICATE' }));
99+
}));
100+
101+
it('should disable identifier validators if External ID is disabled', fakeAsync(() => {
102+
fixture.detectChanges();
103+
component.noticeForm.controls.profileType?.setValue(ProfileType.PUA);
104+
105+
externalIdSubject.next(false);
106+
107+
component.profileTypeSignal.set(ProfileType.PA);
108+
component.profileTypeSignal.set(ProfileType.PUA);
109+
fixture.detectChanges();
110+
111+
const identifierCtrl = component.noticeForm.controls.identifier;
112+
113+
expect(component.externalIdentifierEnabledSignal()).toBeFalse();
114+
115+
expect(identifierCtrl?.hasValidator(Validators.required)).toBeFalse();
116+
expect(identifierCtrl?.hasValidator(Validators.minLength(2))).toBeFalse();
117+
expect(identifierCtrl?.hasValidator(Validators.maxLength(100))).toBeFalse();
118+
119+
identifierCtrl?.setValue('');
120+
expect(identifierCtrl?.valid).toBeTrue();
121+
expect(identifierCtrl?.validator).toBeNull();
122+
}));
123+
124+
it('should check PA and set isIdentifierOK to true if unique', fakeAsync(() => {
125+
component.profileTypeSignal.set(ProfileType.PA);
126+
fixture.detectChanges();
127+
mockProfileService.checkPaProfile.and.returnValue(of(false));
128+
component.noticeForm.controls.identifier?.setValue('UNIQUE_TEST');
129+
130+
expect(mockProfileService.checkPaProfile).toHaveBeenCalledWith(jasmine.objectContaining({ identifier: 'UNIQUE_TEST' }));
131+
expect(component.noticeForm.controls.identifier.invalid).toBeFalse();
132+
}));
133+
134+
it('should check PA and alert if duplicated', fakeAsync(() => {
135+
component.profileTypeSignal.set(ProfileType.PA);
136+
fixture.detectChanges();
137+
mockAppService.isApplicationExternalIdentifierEnabled.and.returnValue(of(true));
138+
mockProfileService.checkPaProfile.and.returnValue(of(true));
139+
140+
component.noticeForm.controls.identifier?.setValue('DUPLICATE_PA');
141+
142+
expect(mockProfileService.checkPaProfile).toHaveBeenCalledWith(jasmine.objectContaining({ identifier: 'DUPLICATE_PA' }));
143+
expect(component.noticeForm.controls.identifier.invalid).toBeTrue();
144+
}));
145+
146+
it('should check PUA and set isIdentifierOK to true if unique', fakeAsync(() => {
147+
component.profileTypeSignal.set(ProfileType.PUA);
148+
fixture.detectChanges();
149+
mockProfileService.checkPuaProfile.and.returnValue(of(false));
150+
component.noticeForm.controls.identifier?.setValue('UNIQUE_PUA');
151+
152+
expect(mockProfileService.checkPuaProfile).toHaveBeenCalledWith(jasmine.objectContaining({ identifier: 'UNIQUE_PUA' }));
153+
expect(component.noticeForm.controls.identifier.invalid).toBeFalse();
154+
}));
155+
156+
it('should check PUA and alert if duplicated', fakeAsync(() => {
157+
component.profileTypeSignal.set(ProfileType.PUA);
158+
fixture.detectChanges();
159+
mockProfileService.checkPuaProfile.and.returnValue(of(true));
160+
component.noticeForm.controls.identifier?.setValue('DUPLICATE_TEST');
161+
162+
expect(mockProfileService.checkPuaProfile).toHaveBeenCalledWith(jasmine.objectContaining({ identifier: 'DUPLICATE_TEST' }));
163+
component.noticeForm.controls.profileType?.setValue(ProfileType.PUA);
164+
expect(component.noticeForm.controls.identifier.invalid).toBeTrue();
165+
}));
166+
167+
it('should create', () => {
168+
expect(component).toBeTruthy();
169+
});
170+
});

0 commit comments

Comments
 (0)