[ADF-5065] people and group picker tests, clean code (#5442)

* cleanup component types

* more readable tests

* clean code

* C291998: render preselected people

* clean code

* extra tests and clean code

* fix test after rebase
This commit is contained in:
Denys Vuika
2020-02-03 15:10:17 +00:00
committed by GitHub
parent d9e0847b18
commit bd3957dcee
9 changed files with 666 additions and 459 deletions

View File

@@ -6,9 +6,9 @@
<mat-card-content> <mat-card-content>
<br> <br>
<mat-radio-group (change)="onChangePeopleMode($event)"> <mat-radio-group (change)="onChangePeopleMode($event)">
<mat-radio-button checked="true" class="app-people-single-mode" data-automation-id="app-people-single-mode" value="{{ peopleSingleMode }}">{{ <mat-radio-button checked="true" class="app-people-single-mode" data-automation-id="app-people-single-mode" value="single">{{
'PEOPLE_GROUPS_CLOUD.SINGLE' | translate }}</mat-radio-button> 'PEOPLE_GROUPS_CLOUD.SINGLE' | translate }}</mat-radio-button>
<mat-radio-button class="app-people-multiple-mode" data-automation-id="app-people-multiple-mode" value="{{ peopleMultipleMode }}">{{ <mat-radio-button class="app-people-multiple-mode" data-automation-id="app-people-multiple-mode" value="multiple">{{
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button> 'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
</mat-radio-group> </mat-radio-group>
<div class="app-people-control-options"> <div class="app-people-control-options">
@@ -20,15 +20,15 @@
</mat-radio-group> </mat-radio-group>
<mat-form-field *ngIf="!isPeopleAppNameSelected()" class="app-preselect-value"> <mat-form-field *ngIf="!isPeopleAppNameSelected()" class="app-preselect-value">
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label> <mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label>
<input matInput (input)="setPeopleRoles($event)" data-automation-id="app-people-roles-input" /> <input matInput (input)="setPeopleRoles($event.target?.value)" data-automation-id="app-people-roles-input" />
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="isPeopleAppNameSelected()" class="app-preselect-value"> <mat-form-field *ngIf="isPeopleAppNameSelected()" class="app-preselect-value">
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label> <mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
<input matInput (input)="setPeopleAppName($event)" data-automation-id="app-people-app-input" /> <input matInput (input)="setPeopleAppName($event.target?.value)" data-automation-id="app-people-app-input" />
</mat-form-field> </mat-form-field>
<mat-form-field class="app-preselect-value-full"> <mat-form-field class="app-preselect-value-full">
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.PRESELECTED_VALUE' | translate }} {{ DEFAULT_PEOPLE_PLACEHOLDER }}</mat-label> <mat-label>{{ 'PEOPLE_GROUPS_CLOUD.PRESELECTED_VALUE' | translate }} {{ DEFAULT_PEOPLE_PLACEHOLDER }}</mat-label>
<input matInput (input)="setPeoplePreselectValue($event)" data-automation-id="app-people-preselect-input" /> <input matInput (input)="setPeoplePreselectValue($event.target?.value)" data-automation-id="app-people-preselect-input" />
</mat-form-field> </mat-form-field>
<mat-checkbox class="app-preselect-value" (change)="onChangePeopleValidation($event)">{{ <mat-checkbox class="app-preselect-value" (change)="onChangePeopleValidation($event)">{{
'PEOPLE_GROUPS_CLOUD.PRESELECT_VALIDATION' | translate }}</mat-checkbox> 'PEOPLE_GROUPS_CLOUD.PRESELECT_VALIDATION' | translate }}</mat-checkbox>
@@ -47,7 +47,7 @@
(warning)="onUsersWarning($event)"></adf-cloud-people> (warning)="onUsersWarning($event)"></adf-cloud-people>
</div> </div>
<div class="app-people-list" *ngIf="canShowPeopleList()"> <div class="app-people-list" *ngIf="peopleMode === 'multiple'">
<h4>{{ 'PEOPLE_GROUPS_CLOUD.ALL_PRESELECTED_USERS' | translate }}</h4> <h4>{{ 'PEOPLE_GROUPS_CLOUD.ALL_PRESELECTED_USERS' | translate }}</h4>
<mat-list role="list"> <mat-list role="list">
<mat-list-item *ngFor="let item of preSelectUsers" role="listitem"> <mat-list-item *ngFor="let item of preSelectUsers" role="listitem">
@@ -77,9 +77,9 @@
<mat-card-content> <mat-card-content>
<br> <br>
<mat-radio-group (change)="onChangeGroupsMode($event)"> <mat-radio-group (change)="onChangeGroupsMode($event)">
<mat-radio-button checked="true" class="app-people-single-mode" data-automation-id="app-group-single-mode" value="{{ groupSingleMode }}">{{ <mat-radio-button checked="true" class="app-people-single-mode" data-automation-id="app-group-single-mode" value="single">{{
'PEOPLE_GROUPS_CLOUD.SINGLE' | translate }}</mat-radio-button> 'PEOPLE_GROUPS_CLOUD.SINGLE' | translate }}</mat-radio-button>
<mat-radio-button class="app-people-multiple-mode" data-automation-id="app-group-multiple-mode" value="{{ groupMultipleMode }}">{{ <mat-radio-button class="app-people-multiple-mode" data-automation-id="app-group-multiple-mode" value="multiple">{{
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button> 'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
</mat-radio-group> </mat-radio-group>
<div class="app-groups-control-options"> <div class="app-groups-control-options">
@@ -91,20 +91,31 @@
</mat-radio-group> </mat-radio-group>
<mat-form-field *ngIf="!isGroupAppNameSelected()" class="app-preselect-value"> <mat-form-field *ngIf="!isGroupAppNameSelected()" class="app-preselect-value">
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label> <mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label>
<input matInput (input)="setGroupRoles($event)" data-automation-id="app-group-roles-input"/> <input matInput
(input)="setGroupRoles($event.target?.value)"
data-automation-id="app-group-roles-input"/>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="isGroupAppNameSelected()" class="app-preselect-value"> <mat-form-field *ngIf="isGroupAppNameSelected()" class="app-preselect-value">
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label> <mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
<input matInput (input)="setGroupAppName($event)" data-automation-id="app-group-app-input"/> <input matInput
(input)="setGroupAppName($event.target?.value)"
data-automation-id="app-group-app-input"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="app-preselect-value-full"> <mat-form-field class="app-preselect-value-full">
<mat-label>Preselect: {{ DEFAULT_GROUP_PLACEHOLDER }}</mat-label> <mat-label>Preselect: {{ DEFAULT_GROUP_PLACEHOLDER }}</mat-label>
<input matInput (input)="setGroupsPreselectValue($event)" data-automation-id="app-group-preselect-input" /> <input matInput
(input)="setGroupsPreselectValue($event.target?.value)"
data-automation-id="app-group-preselect-input" />
</mat-form-field> </mat-form-field>
<mat-checkbox class="app-preselect-value" (change)="onChangeGroupValidation($event)">{{ <mat-checkbox class="app-preselect-value" (change)="onChangeGroupValidation($event)">
'PEOPLE_GROUPS_CLOUD.PRESELECT_VALIDATION' | translate }}</mat-checkbox> {{ 'PEOPLE_GROUPS_CLOUD.PRESELECT_VALIDATION' | translate }}
<mat-checkbox data-automation-id="app-group-readonly" value="{{ groupReadonly }}" (change)="onChangeGroupReadonly($event)">{{ </mat-checkbox>
'PEOPLE_GROUPS_CLOUD.READONLY_MODE' | translate }}</mat-checkbox> <mat-checkbox
data-automation-id="app-group-readonly"
value="{{ groupReadonly }}"
(change)="onChangeGroupReadonly($event)">
{{ 'PEOPLE_GROUPS_CLOUD.READONLY_MODE' | translate }}
</mat-checkbox>
</div> </div>
<div> <div>
<adf-cloud-group <adf-cloud-group
@@ -117,7 +128,7 @@
(warning)="onGroupsWarning($event)"></adf-cloud-group> (warning)="onGroupsWarning($event)"></adf-cloud-group>
</div> </div>
<div class="app-group-list" *ngIf="canShowGroupList()"> <div class="app-group-list" *ngIf="groupMode === 'multiple'">
<h4>{{ 'PEOPLE_GROUPS_CLOUD.ALL_PRESELECTED_GROUPS' | translate }}</h4> <h4>{{ 'PEOPLE_GROUPS_CLOUD.ALL_PRESELECTED_GROUPS' | translate }}</h4>
<mat-list role="list"> <mat-list role="list">
<mat-list-item *ngFor="let item of preSelectGroup" role="listitem"> <mat-list-item *ngFor="let item of preSelectGroup" role="listitem">

View File

@@ -16,7 +16,7 @@
*/ */
import { Component, ViewEncapsulation } from '@angular/core'; import { Component, ViewEncapsulation } from '@angular/core';
import { PeopleCloudComponent, GroupCloudComponent } from '@alfresco/adf-process-services-cloud'; import { ComponentSelectionMode } from '@alfresco/adf-process-services-cloud';
import { MatRadioChange, MatCheckboxChange } from '@angular/material'; import { MatRadioChange, MatCheckboxChange } from '@angular/material';
import { IdentityGroupModel, IdentityUserModel } from '@alfresco/adf-core'; import { IdentityGroupModel, IdentityUserModel } from '@alfresco/adf-core';
@@ -32,83 +32,83 @@ export class PeopleGroupCloudDemoComponent {
DEFAULT_GROUP_PLACEHOLDER: string = `[{"id": "1", "name":"activitiUserGroup"}]`; DEFAULT_GROUP_PLACEHOLDER: string = `[{"id": "1", "name":"activitiUserGroup"}]`;
DEFAULT_PEOPLE_PLACEHOLDER: string = `[{"id": "1", email": "user@user.com", "firstName":"user", "lastName": "lastName", "username": "user"}]`; DEFAULT_PEOPLE_PLACEHOLDER: string = `[{"id": "1", email": "user@user.com", "firstName":"user", "lastName": "lastName", "username": "user"}]`;
peopleMode: string = PeopleCloudComponent.MODE_SINGLE; peopleMode: ComponentSelectionMode = 'single';
preSelectUsers: IdentityUserModel[] = []; preSelectUsers: IdentityUserModel[] = [];
invalidUsers: IdentityUserModel[] = []; invalidUsers: IdentityUserModel[] = [];
peopleRoles: string[] = []; peopleRoles: string[] = [];
peopleAppName: string; peopleAppName: string;
peopleFilterMode: string = this.DEFAULT_FILTER_MODE; peopleFilterMode: string = this.DEFAULT_FILTER_MODE;
peoplePreselectValidation: Boolean = false; peoplePreselectValidation = false;
groupPreselectValidation = false; groupPreselectValidation = false;
peopleReadonly = false; peopleReadonly = false;
groupReadonly = false; groupReadonly = false;
groupMode: string = GroupCloudComponent.MODE_SINGLE; groupMode: ComponentSelectionMode = 'single';
preSelectGroup: IdentityGroupModel[] = []; preSelectGroup: IdentityGroupModel[] = [];
invalidGroups: IdentityGroupModel[] = []; invalidGroups: IdentityGroupModel[] = [];
groupRoles: string[]; groupRoles: string[];
groupAppName: string; groupAppName: string;
groupFilterMode: string = this.DEFAULT_FILTER_MODE; groupFilterMode: string = this.DEFAULT_FILTER_MODE;
setPeoplePreselectValue(event: any) { setPeoplePreselectValue(value: string): void {
this.preSelectUsers = this.getArrayFromString(event.target.value); this.preSelectUsers = this.getArrayFromString(value);
} }
setGroupsPreselectValue(event: any) { setGroupsPreselectValue(value: string): void {
this.preSelectGroup = this.getArrayFromString(event.target.value); this.preSelectGroup = this.getArrayFromString(value);
} }
setPeopleRoles(event: any) { setPeopleRoles(value: string): void {
this.peopleRoles = this.getArrayFromString(event.target.value); this.peopleRoles = this.getArrayFromString(value);
} }
setGroupRoles(event: any) { setGroupRoles(value: string): void {
this.groupRoles = this.getArrayFromString(event.target.value); this.groupRoles = this.getArrayFromString(value);
} }
setPeopleAppName(event: any) { setPeopleAppName(value: string): void {
this.peopleAppName = event.target.value; this.peopleAppName = value;
} }
setGroupAppName(event: any) { setGroupAppName(value: string): void {
this.groupAppName = event.target.value; this.groupAppName = value;
} }
onChangePeopleMode(event: MatRadioChange) { onChangePeopleMode(event: MatRadioChange): void {
this.peopleMode = event.value; this.peopleMode = event.value;
} }
onChangePeopleReadonly(event: MatCheckboxChange) { onChangePeopleReadonly(event: MatCheckboxChange): void {
this.peopleReadonly = event.checked; this.peopleReadonly = event.checked;
} }
onChangeGroupReadonly(event: MatCheckboxChange) { onChangeGroupReadonly(event: MatCheckboxChange): void {
this.groupReadonly = event.checked; this.groupReadonly = event.checked;
} }
onChangeGroupsMode(event: MatRadioChange) { onChangeGroupsMode(event: MatRadioChange): void {
this.groupMode = event.value; this.groupMode = event.value;
} }
onChangePeopleFilterMode(event: MatRadioChange) { onChangePeopleFilterMode(event: MatRadioChange): void {
this.peopleFilterMode = event.value; this.peopleFilterMode = event.value;
this.resetPeopleFilter(); this.resetPeopleFilter();
} }
onChangeGroupsFilterMode(event: MatRadioChange) { onChangeGroupsFilterMode(event: MatRadioChange): void {
this.groupFilterMode = event.value; this.groupFilterMode = event.value;
this.restGroupFilter(); this.restGroupFilter();
} }
isPeopleAppNameSelected() { isPeopleAppNameSelected(): boolean {
return this.peopleFilterMode === 'appName'; return this.peopleFilterMode === 'appName';
} }
isGroupAppNameSelected() { isGroupAppNameSelected(): boolean {
return this.groupFilterMode === 'appName'; return this.groupFilterMode === 'appName';
} }
resetPeopleFilter() { resetPeopleFilter(): void {
if (this.isPeopleAppNameSelected()) { if (this.isPeopleAppNameSelected()) {
this.peopleRoles = []; this.peopleRoles = [];
} else { } else {
@@ -116,7 +116,7 @@ export class PeopleGroupCloudDemoComponent {
} }
} }
restGroupFilter() { restGroupFilter(): void {
if (this.isGroupAppNameSelected()) { if (this.isGroupAppNameSelected()) {
this.groupRoles = []; this.groupRoles = [];
} else { } else {
@@ -124,23 +124,23 @@ export class PeopleGroupCloudDemoComponent {
} }
} }
onChangePeopleValidation(event: MatCheckboxChange) { onChangePeopleValidation(event: MatCheckboxChange): void {
this.peoplePreselectValidation = event.checked; this.peoplePreselectValidation = event.checked;
} }
onChangeGroupValidation(event: MatCheckboxChange) { onChangeGroupValidation(event: MatCheckboxChange): void {
this.groupPreselectValidation = event.checked; this.groupPreselectValidation = event.checked;
} }
onGroupsWarning(warning: any) { onGroupsWarning(warning: any): void {
this.invalidGroups = warning.groups; this.invalidGroups = warning.groups;
} }
onUsersWarning(warning: any) { onUsersWarning(warning: any): void {
this.invalidUsers = warning.users; this.invalidUsers = warning.users;
} }
isStringArray(str: string) { isStringArray(str: string): boolean {
try { try {
const result = JSON.parse(str); const result = JSON.parse(str);
return Array.isArray(result); return Array.isArray(result);
@@ -149,36 +149,11 @@ export class PeopleGroupCloudDemoComponent {
} }
} }
private getArrayFromString(value: string) { private getArrayFromString<T = any>(value: string): T[] {
if (this.isStringArray(value)) { if (this.isStringArray(value)) {
return JSON.parse(value); return JSON.parse(value);
} else { } else {
return []; return [];
} }
} }
canShowPeopleList() {
return this.peopleMode === GroupCloudComponent.MODE_MULTIPLE;
}
canShowGroupList() {
return this.groupMode === GroupCloudComponent.MODE_MULTIPLE;
}
get peopleSingleMode() {
return PeopleCloudComponent.MODE_SINGLE;
}
get peopleMultipleMode() {
return PeopleCloudComponent.MODE_MULTIPLE;
}
get groupSingleMode() {
return GroupCloudComponent.MODE_SINGLE;
}
get groupMultipleMode() {
return GroupCloudComponent.MODE_MULTIPLE;
}
} }

View File

@@ -200,7 +200,6 @@ describe('People Groups Cloud Component', () => {
await peopleGroupCloudComponentPage.clickPreselectValidation(); await peopleGroupCloudComponentPage.clickPreselectValidation();
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true'); await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${noRoleUser.idIdentityService}"}]`); await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${noRoleUser.idIdentityService}"}]`);
await expect(await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`)); await expect(await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`));

View File

@@ -45,13 +45,15 @@ describe('GroupCloudComponent', () => {
} }
}; };
function getElement<T = HTMLElement>(selector: string): T {
return <T> fixture.nativeElement.querySelector(selector);
}
setupTestBed({ setupTestBed({
imports: [ imports: [
CoreModule.forRoot(), CoreModule.forRoot(),
ProcessServiceCloudTestingModule, ProcessServiceCloudTestingModule,
GroupCloudModule], GroupCloudModule
providers: [
IdentityGroupService
] ]
}); });
@@ -59,19 +61,19 @@ describe('GroupCloudComponent', () => {
fixture = TestBed.createComponent(GroupCloudComponent); fixture = TestBed.createComponent(GroupCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
element = fixture.nativeElement; element = fixture.nativeElement;
identityGroupService = TestBed.get(IdentityGroupService); identityGroupService = TestBed.get(IdentityGroupService);
alfrescoApiService = TestBed.get(AlfrescoApiService); alfrescoApiService = TestBed.get(AlfrescoApiService);
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
});
it('should create GroupCloudComponent', () => { spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
expect(component instanceof GroupCloudComponent).toBeTruthy();
}); });
it('should populate placeholder when title is present', async(() => { it('should populate placeholder when title is present', async(() => {
component.title = 'TITLE_KEY'; component.title = 'TITLE_KEY';
fixture.detectChanges(); fixture.detectChanges();
const matLabel: HTMLInputElement = <HTMLInputElement> element.querySelector('#adf-group-cloud-title-id'); const matLabel: HTMLInputElement = <HTMLInputElement> element.querySelector('#adf-group-cloud-title-id');
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(matLabel.textContent).toEqual('TITLE_KEY'); expect(matLabel.textContent).toEqual('TITLE_KEY');
@@ -82,18 +84,17 @@ describe('GroupCloudComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement;
findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
})); }));
it('should list the groups as dropdown options if the search term has results', (done) => { it('should list the groups as dropdown options if the search term has results', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'Mock'; input.value = 'Mock';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges();
fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5);
@@ -124,11 +125,13 @@ describe('GroupCloudComponent', () => {
it('should hide result list if input is empty', (done) => { it('should hide result list if input is empty', (done) => {
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = ''; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = '';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(element.querySelector('mat-option')).toBeNull(); expect(element.querySelector('mat-option')).toBeNull();
@@ -136,33 +139,76 @@ describe('GroupCloudComponent', () => {
}); });
}); });
it('should selectedGroup and groupsChanged emit, update selected groups when a group is selected', (done) => { it('should update selected groups when a group is selected', (done) => {
const group = { name: 'groupname' };
fixture.detectChanges();
const selectEmitSpy = spyOn(component.selectGroup, 'emit');
const changedGroupsSpy = spyOn(component.changedGroups, 'emit');
component.onSelect(group);
fixture.detectChanges(); fixture.detectChanges();
const selectEmitSpy = spyOn(component.selectGroup, 'emit');
const changedGroupsSpy = spyOn(component.changedGroups, 'emit');
const group = { name: 'groupname' };
component.onSelect(group);
fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(selectEmitSpy).toHaveBeenCalledWith(group); expect(selectEmitSpy).toHaveBeenCalledWith(group);
expect(changedGroupsSpy).toHaveBeenCalledWith([group]); expect(changedGroupsSpy).toHaveBeenCalledWith([group]);
expect(component.getSelectedGroups()[0]).toEqual(group); expect(component.selectedGroups).toEqual([group]);
done(); done();
}); });
}); });
it('should replace the group in single-selection mode', () => {
component.mode = 'single';
const group1 = { name: 'group1' };
const group2 = { name: 'group2' };
component.onSelect(group1);
expect(component.selectedGroups).toEqual([group1]);
component.onSelect(group2);
expect(component.selectedGroups).toEqual([group2]);
});
it('should allow multiple groups in multi-selection mode', () => {
component.mode = 'multiple';
const group1 = { name: 'group1' };
const group2 = { name: 'group2' };
component.onSelect(group1);
component.onSelect(group2);
expect(component.selectedGroups).toEqual([group1, group2]);
});
it('should allow only unique groups in multi-selection mode', () => {
component.mode = 'multiple';
const group1 = { name: 'group1' };
const group2 = { name: 'group2' };
component.onSelect(group1);
component.onSelect(group2);
component.onSelect(group1);
component.onSelect(group2);
expect(component.selectedGroups).toEqual([group1, group2]);
});
it('should show an error message if the search result empty', (done) => { it('should show an error message if the search result empty', (done) => {
findGroupsByNameSpy.and.returnValue(of([])); findGroupsByNameSpy.and.returnValue(of([]));
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'ZZZ'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'ZZZ';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
inputHTMLElement.blur(); input.blur();
fixture.detectChanges(); fixture.detectChanges();
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]'); const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
expect(errorMessage).not.toBeNull(); expect(errorMessage).not.toBeNull();
@@ -173,7 +219,6 @@ describe('GroupCloudComponent', () => {
}); });
describe('when application name defined', () => { describe('when application name defined', () => {
let checkGroupHasAnyClientAppRoleSpy: jasmine.Spy; let checkGroupHasAnyClientAppRoleSpy: jasmine.Spy;
let checkGroupHasClientAppSpy: jasmine.Spy; let checkGroupHasClientAppSpy: jasmine.Spy;
@@ -181,17 +226,20 @@ describe('GroupCloudComponent', () => {
findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
checkGroupHasAnyClientAppRoleSpy = spyOn(identityGroupService, 'checkGroupHasAnyClientAppRole').and.returnValue(of(true)); checkGroupHasAnyClientAppRoleSpy = spyOn(identityGroupService, 'checkGroupHasAnyClientAppRole').and.returnValue(of(true));
checkGroupHasClientAppSpy = spyOn(identityGroupService, 'checkGroupHasClientApp').and.returnValue(of(true)); checkGroupHasClientAppSpy = spyOn(identityGroupService, 'checkGroupHasClientApp').and.returnValue(of(true));
component.preSelectGroups = []; component.preSelectGroups = [];
component.appName = 'mock-app-name'; component.appName = 'mock-app-name';
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement;
})); }));
it('should fetch the client ID if appName specified', async (() => { it('should fetch the client ID if appName specified', async (() => {
const getClientIdByApplicationNameSpy = spyOn(identityGroupService, 'getClientIdByApplicationName').and.callThrough(); const getClientIdByApplicationNameSpy = spyOn(identityGroupService, 'getClientIdByApplicationName').and.callThrough();
component.appName = 'mock-app-name'; component.appName = 'mock-app-name';
const change = new SimpleChange(null, 'mock-app-name', false); const change = new SimpleChange(null, 'mock-app-name', false);
component.ngOnChanges({ 'appName': change }); component.ngOnChanges({ 'appName': change });
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -200,11 +248,12 @@ describe('GroupCloudComponent', () => {
})); }));
it('should list groups who have access to the app when appName is specified', (done) => { it('should list groups who have access to the app when appName is specified', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'M'; input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -216,11 +265,13 @@ describe('GroupCloudComponent', () => {
it('should not list groups who do not have access to the app when appName is specified', (done) => { it('should not list groups who do not have access to the app when appName is specified', (done) => {
checkGroupHasClientAppSpy.and.returnValue(of(false)); checkGroupHasClientAppSpy.and.returnValue(of(false));
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -231,11 +282,13 @@ describe('GroupCloudComponent', () => {
it('should list groups if given roles mapped with client roles', (done) => { it('should list groups if given roles mapped with client roles', (done) => {
component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1']; component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1'];
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -248,11 +301,13 @@ describe('GroupCloudComponent', () => {
it('should not list groups if roles are not mapping with client roles', (done) => { it('should not list groups if roles are not mapping with client roles', (done) => {
checkGroupHasAnyClientAppRoleSpy.and.returnValue(of(false)); checkGroupHasAnyClientAppRoleSpy.and.returnValue(of(false));
component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1']; component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1'];
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -263,11 +318,12 @@ describe('GroupCloudComponent', () => {
}); });
it('should not call client role mapping sevice if roles not specified', (done) => { it('should not call client role mapping sevice if roles not specified', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'M'; input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -277,11 +333,12 @@ describe('GroupCloudComponent', () => {
}); });
it('should validate access to the app when appName is specified', (done) => { it('should validate access to the app when appName is specified', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'M'; input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -293,11 +350,13 @@ describe('GroupCloudComponent', () => {
it('should not validate access to the app when appName is not specified', (done) => { it('should not validate access to the app when appName is not specified', (done) => {
component.appName = ''; component.appName = '';
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -310,18 +369,22 @@ describe('GroupCloudComponent', () => {
checkGroupHasClientAppSpy.and.returnValue(of(false)); checkGroupHasClientAppSpy.and.returnValue(of(false));
findGroupsByNameSpy.and.returnValue(of([])); findGroupsByNameSpy.and.returnValue(of([]));
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'ZZZ'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'ZZZ';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
inputHTMLElement.blur(); input.blur();
fixture.detectChanges(); fixture.detectChanges();
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]'); const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
expect(errorMessage).not.toBeNull(); expect(errorMessage).not.toBeNull();
expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUND'); expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUND');
done(); done();
}); });
}); });
@@ -335,6 +398,7 @@ describe('GroupCloudComponent', () => {
it('should not pre-select any group when preSelectGroups is empty - single mode', () => { it('should not pre-select any group when preSelectGroups is empty - single mode', () => {
component.mode = 'single'; component.mode = 'single';
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
expect(chips.length).toEqual(0); expect(chips.length).toEqual(0);
}); });
@@ -342,13 +406,13 @@ describe('GroupCloudComponent', () => {
it('should not pre-select any group when preSelectGroups is empty - multiple mode', () => { it('should not pre-select any group when preSelectGroups is empty - multiple mode', () => {
component.mode = 'multiple'; component.mode = 'multiple';
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
expect(chips.length).toEqual(0); expect(chips.length).toEqual(0);
}); });
}); });
describe('When roles defined', () => { describe('When roles defined', () => {
let checkGroupHasRoleSpy: jasmine.Spy; let checkGroupHasRoleSpy: jasmine.Spy;
beforeEach(async(() => { beforeEach(async(() => {
@@ -356,15 +420,16 @@ describe('GroupCloudComponent', () => {
spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
checkGroupHasRoleSpy = spyOn(identityGroupService, 'checkGroupHasRole').and.returnValue(of(true)); checkGroupHasRoleSpy = spyOn(identityGroupService, 'checkGroupHasRole').and.returnValue(of(true));
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement;
})); }));
it('should filter if groups has any specified role', (done) => { it('should filter if groups has any specified role', (done) => {
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('input')); input.value = 'M';
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -377,10 +442,12 @@ describe('GroupCloudComponent', () => {
it('should not filter groups if group does not have any specified role', (done) => { it('should not filter groups if group does not have any specified role', (done) => {
fixture.detectChanges(); fixture.detectChanges();
checkGroupHasRoleSpy.and.returnValue(of(false)); checkGroupHasRoleSpy.and.returnValue(of(false));
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('input')); input.value = 'M';
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -393,10 +460,12 @@ describe('GroupCloudComponent', () => {
it('should not call checkGroupHasRole service when roles are not specified', (done) => { it('should not call checkGroupHasRole service when roles are not specified', (done) => {
component.roles = []; component.roles = [];
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('input')); input.value = 'M';
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -407,7 +476,6 @@ describe('GroupCloudComponent', () => {
}); });
describe('Single Mode with pre-selected groups', () => { describe('Single Mode with pre-selected groups', () => {
const changes = new SimpleChange(null, mockIdentityGroups, false); const changes = new SimpleChange(null, mockIdentityGroups, false);
beforeEach(async(() => { beforeEach(async(() => {
@@ -415,7 +483,6 @@ describe('GroupCloudComponent', () => {
component.preSelectGroups = <any> mockIdentityGroups; component.preSelectGroups = <any> mockIdentityGroups;
component.ngOnChanges({ 'preSelectGroups': changes }); component.ngOnChanges({ 'preSelectGroups': changes });
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement;
})); }));
it('should show only one mat chip with the first preSelectedGroup', () => { it('should show only one mat chip with the first preSelectedGroup', () => {
@@ -426,7 +493,6 @@ describe('GroupCloudComponent', () => {
}); });
describe('Multiple Mode with pre-selected groups', () => { describe('Multiple Mode with pre-selected groups', () => {
const change = new SimpleChange(null, mockIdentityGroups, false); const change = new SimpleChange(null, mockIdentityGroups, false);
beforeEach(async(() => { beforeEach(async(() => {
@@ -434,10 +500,9 @@ describe('GroupCloudComponent', () => {
component.preSelectGroups = <any> mockIdentityGroups; component.preSelectGroups = <any> mockIdentityGroups;
component.ngOnChanges({ 'preSelectGroups': change }); component.ngOnChanges({ 'preSelectGroups': change });
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement;
})); }));
it('should pre-select all preSelectGroups', () => { it('should render all preselected groups', () => {
component.mode = 'multiple'; component.mode = 'multiple';
fixture.detectChanges(); fixture.detectChanges();
component.ngOnChanges({ 'preSelectGroups': change }); component.ngOnChanges({ 'preSelectGroups': change });
@@ -460,7 +525,7 @@ describe('GroupCloudComponent', () => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(removeGroupEmitterSpy).toHaveBeenCalledWith(groupToRemove); expect(removeGroupEmitterSpy).toHaveBeenCalledWith(groupToRemove);
expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockIdentityGroups[1], mockIdentityGroups[2], mockIdentityGroups[3], mockIdentityGroups[4]]); expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockIdentityGroups[1], mockIdentityGroups[2], mockIdentityGroups[3], mockIdentityGroups[4]]);
expect(component.getSelectedGroups().indexOf({ expect(component.selectedGroups.indexOf({
id: groupToRemove.id, id: groupToRemove.id,
name: groupToRemove.name, name: groupToRemove.name,
path: groupToRemove.path path: groupToRemove.path
@@ -482,42 +547,54 @@ describe('GroupCloudComponent', () => {
const change = new SimpleChange(null, component.preSelectGroups, false); const change = new SimpleChange(null, component.preSelectGroups, false);
component.mode = 'multiple'; component.mode = 'multiple';
component.ngOnChanges({ 'preSelectGroups': change }); component.ngOnChanges({ 'preSelectGroups': change });
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip'); const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
const removeIcon = <HTMLElement> fixture.nativeElement.querySelector('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]'); const removeIcon = getElement('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]');
expect(chipList.length).toBe(2); expect(chipList.length).toBe(2);
expect(component.preSelectGroups[0].readonly).toBeTruthy(); expect(component.preSelectGroups[0].readonly).toBeTruthy();
expect(component.preSelectGroups[1].readonly).toBeTruthy(); expect(component.preSelectGroups[1].readonly).toBeTruthy();
expect(removeIcon).toBeNull(); expect(removeIcon).toBeNull();
done(); done();
}); });
}); });
it('Should be able to remove preselected groups if readonly property set to false', (done) => { it('Should be able to remove preselected groups if readonly property set to false', (done) => {
fixture.detectChanges(); fixture.detectChanges();
component.preSelectGroups = [ component.preSelectGroups = [
{ id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: false }, { id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: false },
{ id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: false } { id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: false }
]; ];
const change = new SimpleChange(null, component.preSelectGroups, false); const change = new SimpleChange(null, component.preSelectGroups, false);
component.mode = 'multiple'; component.mode = 'multiple';
component.ngOnChanges({ 'preSelectGroups': change }); component.ngOnChanges({ 'preSelectGroups': change });
const removeGroupSpy = spyOn(component.removeGroup, 'emit'); const removeGroupSpy = spyOn(component.removeGroup, 'emit');
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const removeIcon = <HTMLElement> fixture.nativeElement.querySelector('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]'); const removeIcon = getElement('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]');
expect(chips.length).toBe(2); expect(chips.length).toBe(2);
expect(component.preSelectGroups[0].readonly).toBe(false, 'Removable'); expect(component.preSelectGroups[0].readonly).toBe(false, 'Removable');
expect(component.preSelectGroups[1].readonly).toBe(false, 'Removable'); expect(component.preSelectGroups[1].readonly).toBe(false, 'Removable');
removeIcon.click(); removeIcon.click();
fixture.detectChanges(); fixture.detectChanges();
expect(removeGroupSpy).toHaveBeenCalled(); expect(removeGroupSpy).toHaveBeenCalled();
expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1); expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1);
done(); done();
}); });
}); });
@@ -532,8 +609,10 @@ describe('GroupCloudComponent', () => {
component.ngOnChanges({ 'preSelectGroups': change }); component.ngOnChanges({ 'preSelectGroups': change });
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chipList = fixture.nativeElement.querySelector('mat-chip-list'); const chipList = getElement('mat-chip-list');
expect(chips).toBeDefined(); expect(chips).toBeDefined();
expect(chipList).toBeDefined(); expect(chipList).toBeDefined();
expect(chips.length).toBe(1); expect(chips.length).toBe(1);
@@ -545,9 +624,12 @@ describe('GroupCloudComponent', () => {
component.readOnly = true; component.readOnly = true;
component.preSelectGroups = <any> mockIdentityGroups; component.preSelectGroups = <any> mockIdentityGroups;
component.ngOnChanges({ 'preSelectGroups': change }); component.ngOnChanges({ 'preSelectGroups': change });
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chipList = fixture.nativeElement.querySelector('mat-chip-list'); const chipList = getElement('mat-chip-list');
expect(chips).toBeDefined(); expect(chips).toBeDefined();
expect(chipList).toBeDefined(); expect(chipList).toBeDefined();
expect(chips.length).toBe(5); expect(chips.length).toBe(5);
@@ -608,7 +690,9 @@ describe('GroupCloudComponent', () => {
component.mode = 'multiple'; component.mode = 'multiple';
component.validate = true; component.validate = true;
component.preSelectGroups = <any> [mockIdentityGroups[0], mockIdentityGroups[1]]; component.preSelectGroups = <any> [mockIdentityGroups[0], mockIdentityGroups[1]];
component.ngOnChanges({ 'preSelectGroups': new SimpleChange(null, [mockIdentityGroups[0], mockIdentityGroups[1]], false) }); component.ngOnChanges({
'preSelectGroups': new SimpleChange(null, [mockIdentityGroups[0], mockIdentityGroups[1]], false)
});
}); });
}); });

View File

@@ -35,12 +35,8 @@ import { trigger, state, style, transition, animate } from '@angular/animations'
import { Observable, of, BehaviorSubject, Subject } from 'rxjs'; import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
import { debounceTime } from 'rxjs/internal/operators/debounceTime'; import { debounceTime } from 'rxjs/internal/operators/debounceTime';
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map, takeUntil } from 'rxjs/operators'; import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map, takeUntil } from 'rxjs/operators';
import { import { IdentityGroupModel, IdentityGroupService, LogService } from '@alfresco/adf-core';
IdentityGroupModel, import { ComponentSelectionMode } from '../../types';
IdentityGroupSearchParam,
IdentityGroupService,
LogService
} from '@alfresco/adf-core';
@Component({ @Component({
selector: 'adf-cloud-group', selector: 'adf-cloud-group',
@@ -60,9 +56,6 @@ import {
}) })
export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy { export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
static MODE_SINGLE = 'single';
static MODE_MULTIPLE = 'multiple';
/** Name of the application. If specified this shows the groups who have access to the app. */ /** Name of the application. If specified this shows the groups who have access to the app. */
@Input() @Input()
appName: string; appName: string;
@@ -73,7 +66,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
/** Group selection mode (single/multiple). */ /** Group selection mode (single/multiple). */
@Input() @Input()
mode: string = GroupCloudComponent.MODE_SINGLE; mode: ComponentSelectionMode = 'single';
/** Array of groups to be pre-selected. This pre-selects all groups in multi selection mode and only the first group of the array in single selection mode. */ /** Array of groups to be pre-selected. This pre-selects all groups in multi selection mode and only the first group of the array in single selection mode. */
@Input() @Input()
@@ -119,18 +112,16 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
private groupInput: ElementRef<HTMLInputElement>; private groupInput: ElementRef<HTMLInputElement>;
private searchGroups: IdentityGroupModel[] = []; private searchGroups: IdentityGroupModel[] = [];
private searchGroupsSubject: BehaviorSubject<IdentityGroupModel[]>;
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
selectedGroups: IdentityGroupModel[] = []; selectedGroups: IdentityGroupModel[] = [];
invalidGroups: IdentityGroupModel[] = []; invalidGroups: IdentityGroupModel[] = [];
searchGroups$: Observable<IdentityGroupModel[]>; searchGroups$ = new BehaviorSubject<IdentityGroupModel[]>(this.searchGroups);
_subscriptAnimationState = 'enter'; _subscriptAnimationState = 'enter';
clientId: string; clientId: string;
isFocused: boolean; isFocused: boolean;
currentTimeout: any;
validateGroupsMessage: string; validateGroupsMessage: string;
searchedValue = ''; searchedValue = '';
@@ -140,18 +131,12 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
private identityGroupService: IdentityGroupService, private identityGroupService: IdentityGroupService,
private logService: LogService) {} private logService: LogService) {}
ngOnInit() { ngOnInit(): void {
if (this.searchGroupsSubject === undefined) {
this.searchGroupsSubject = new BehaviorSubject<IdentityGroupModel[]>(this.searchGroups);
this.searchGroups$ = this.searchGroupsSubject.asObservable();
}
this.loadClientId(); this.loadClientId();
this.initSearch(); this.initSearch();
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges): void {
if (this.hasPreselectedGroupsChanged(changes) || this.hasModeChanged(changes) || this.isValidationChanged(changes)) { if (this.hasPreselectedGroupsChanged(changes) || this.hasModeChanged(changes) || this.isValidationChanged(changes)) {
if (this.hasPreSelectGroups()) { if (this.hasPreSelectGroups()) {
this.loadPreSelectGroups(); this.loadPreSelectGroups();
@@ -172,17 +157,21 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
private isAppNameChanged(change: SimpleChange): boolean { private isAppNameChanged(change: SimpleChange): boolean {
return change && change.previousValue !== change.currentValue && this.appName && this.appName.length > 0; return change
&& change.previousValue !== change.currentValue
&& this.appName
&& this.appName.length > 0;
} }
private async loadClientId() { private async loadClientId(): Promise<void> {
this.clientId = await this.identityGroupService.getClientIdByApplicationName(this.appName).toPromise(); this.clientId = await this.identityGroupService.getClientIdByApplicationName(this.appName).toPromise();
if (this.clientId) { if (this.clientId) {
this.searchGroupsControl.enable(); this.searchGroupsControl.enable();
} }
} }
initSearch() { initSearch(): void {
this.searchGroupsControl.valueChanges.pipe( this.searchGroupsControl.valueChanges.pipe(
debounceTime(500), debounceTime(500),
distinctUntilChanged(), distinctUntilChanged(),
@@ -198,26 +187,21 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
this.searchGroupsControl.markAsUntouched(); this.searchGroupsControl.markAsUntouched();
} }
}), }),
tap(() => { tap(() => this.resetSearchGroups()),
this.resetSearchGroups(); switchMap((name: string) =>
}), this.identityGroupService.findGroupsByName({ name: name.trim() })
switchMap((inputValue) => { ),
const queryParams = this.createSearchParam(inputValue);
return this.identityGroupService.findGroupsByName(queryParams);
}),
mergeMap((groups) => { mergeMap((groups) => {
this.resetSearchGroups(); this.resetSearchGroups();
return groups; return groups;
}), }),
filter((group: any) => { filter(group => !this.isGroupAlreadySelected(group)),
return !this.isGroupAlreadySelected(group); mergeMap(group => {
}),
mergeMap((group: any) => {
if (this.appName) { if (this.appName) {
return this.checkGroupHasAccess(group.id).pipe( return this.checkGroupHasAccess(group.id).pipe(
mergeMap((hasRole) => { mergeMap(
return hasRole ? of(group) : of(); hasRole => hasRole ? of(group) : of()
}) )
); );
} else if (this.hasRoles()) { } else if (this.hasRoles()) {
return this.filterGroupsByRoles(group); return this.filterGroupsByRoles(group);
@@ -226,9 +210,9 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
}), }),
takeUntil(this.onDestroy$) takeUntil(this.onDestroy$)
).subscribe((searchedGroup: any) => { ).subscribe(searchedGroup => {
this.searchGroups.push(searchedGroup); this.searchGroups.push(searchedGroup);
this.searchGroupsSubject.next(this.searchGroups); this.searchGroups$.next(this.searchGroups);
}); });
} }
@@ -251,22 +235,22 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
return false; return false;
} }
async searchGroup(groupName: string): Promise<IdentityGroupModel> { async searchGroup(name: string): Promise<IdentityGroupModel> {
return (await this.identityGroupService.findGroupsByName({ name: groupName }).toPromise())[0]; return (await this.identityGroupService.findGroupsByName({ name }).toPromise())[0];
}
private getPreselectedGroups(): IdentityGroupModel[] {
if (this.isSingleMode()) {
return [this.preSelectGroups[0]];
} else {
return this.removeDuplicatedGroups(this.preSelectGroups);
}
} }
async validatePreselectGroups(): Promise<any> { async validatePreselectGroups(): Promise<any> {
this.invalidGroups = []; this.invalidGroups = [];
let preselectedGroupsToValidate: IdentityGroupModel[] = []; for (const group of this.getPreselectedGroups()) {
if (this.isSingleMode()) {
preselectedGroupsToValidate = [this.preSelectGroups[0]];
} else {
preselectedGroupsToValidate = this.removeDuplicatedGroups(this.preSelectGroups);
}
await Promise.all(preselectedGroupsToValidate.map(async (group: IdentityGroupModel) => {
try { try {
const validationResult = await this.searchGroup(group.name); const validationResult = await this.searchGroup(group.name);
if (this.isPreselectedGroupInvalid(group, validationResult)) { if (this.isPreselectedGroupInvalid(group, validationResult)) {
@@ -276,13 +260,12 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
this.invalidGroups.push(group); this.invalidGroups.push(group);
this.logService.error(error); this.logService.error(error);
} }
})); }
this.checkPreselectValidationErrors(); this.checkPreselectValidationErrors();
this.isLoading = false;
} }
public checkPreselectValidationErrors() { checkPreselectValidationErrors(): void {
this.invalidGroups = this.removeDuplicatedGroups(this.invalidGroups); this.invalidGroups = this.removeDuplicatedGroups(this.invalidGroups);
if (this.invalidGroups.length > 0) { if (this.invalidGroups.length > 0) {
@@ -295,7 +278,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
generateInvalidGroupsMessage() { generateInvalidGroupsMessage(): void {
this.validateGroupsMessage = ''; this.validateGroupsMessage = '';
this.invalidGroups.forEach((invalidGroup: IdentityGroupModel, index) => { this.invalidGroups.forEach((invalidGroup: IdentityGroupModel, index) => {
@@ -307,7 +290,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
private async loadPreSelectGroups() { private async loadPreSelectGroups(): Promise<void> {
this.selectedGroups = []; this.selectedGroups = [];
if (this.isSingleMode()) { if (this.isSingleMode()) {
@@ -319,6 +302,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
if (this.isValidationEnabled()) { if (this.isValidationEnabled()) {
this.isLoading = true; this.isLoading = true;
await this.validatePreselectGroups(); await this.validatePreselectGroups();
this.isLoading = false;
} }
} }
@@ -329,8 +313,9 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
map((filteredGroup: { hasRole: boolean, group: IdentityGroupModel }) => filteredGroup.group)); map((filteredGroup: { hasRole: boolean, group: IdentityGroupModel }) => filteredGroup.group));
} }
onSelect(group: IdentityGroupModel) { onSelect(group: IdentityGroupModel): void {
this.selectGroup.emit(group); this.selectGroup.emit(group);
if (this.isMultipleMode()) { if (this.isMultipleMode()) {
if (!this.isGroupAlreadySelected(group)) { if (!this.isGroupAlreadySelected(group)) {
this.selectedGroups.push(group); this.selectedGroups.push(group);
@@ -347,7 +332,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
this.resetSearchGroups(); this.resetSearchGroups();
} }
onRemove(groupToRemove: IdentityGroupModel) { onRemove(groupToRemove: IdentityGroupModel): void {
this.removeGroup.emit(groupToRemove); this.removeGroup.emit(groupToRemove);
this.removeGroupFromSelected(groupToRemove); this.removeGroupFromSelected(groupToRemove);
this.changedGroups.emit(this.selectedGroups); this.changedGroups.emit(this.selectedGroups);
@@ -359,18 +344,19 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
private removeGroupFromSelected(groupToRemove: IdentityGroupModel) { private removeGroupFromSelected({ id, name }: IdentityGroupModel): void {
const indexToRemove = this.selectedGroups.findIndex((selectedGroup: IdentityGroupModel) => { const indexToRemove = this.selectedGroups.findIndex(group => {
return selectedGroup.id === groupToRemove.id && selectedGroup.name === groupToRemove.name; return group.id === id && group.name === name;
}); });
if (indexToRemove !== -1) { if (indexToRemove !== -1) {
this.selectedGroups.splice(indexToRemove, 1); this.selectedGroups.splice(indexToRemove, 1);
} }
} }
private removeGroupFromValidation(groupToRemove: IdentityGroupModel) { private removeGroupFromValidation({ id, name }: IdentityGroupModel): void {
const indexToRemove = this.invalidGroups.findIndex((invalidGroup) => { const indexToRemove = this.invalidGroups.findIndex(group => {
return invalidGroup.name === groupToRemove.name && invalidGroup.id === groupToRemove.id; return group.id === id && group.name === name;
}); });
if (indexToRemove !== -1) { if (indexToRemove !== -1) {
@@ -378,9 +364,9 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
private resetSearchGroups() { private resetSearchGroups(): void {
this.searchGroups = []; this.searchGroups = [];
this.searchGroupsSubject.next(this.searchGroups); this.searchGroups$.next(this.searchGroups);
} }
isPreselectedGroupInvalid(preselectedGroup: IdentityGroupModel, validatedGroup: IdentityGroupModel): boolean { isPreselectedGroupInvalid(preselectedGroup: IdentityGroupModel, validatedGroup: IdentityGroupModel): boolean {
@@ -392,7 +378,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
isSingleMode(): boolean { isSingleMode(): boolean {
return this.mode === GroupCloudComponent.MODE_SINGLE; return this.mode === 'single';
} }
private isSingleSelectionReadonly(): boolean { private isSingleSelectionReadonly(): boolean {
@@ -408,7 +394,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
isMultipleMode(): boolean { isMultipleMode(): boolean {
return this.mode === GroupCloudComponent.MODE_MULTIPLE; return this.mode === 'multiple';
} }
getDisplayName(group: IdentityGroupModel): string { getDisplayName(group: IdentityGroupModel): string {
@@ -426,37 +412,39 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
return this.preSelectGroups && this.preSelectGroups.length > 0; return this.preSelectGroups && this.preSelectGroups.length > 0;
} }
private hasModeChanged(changes): boolean { private hasModeChanged(changes: SimpleChanges): boolean {
return changes && changes.mode && changes.mode.currentValue !== changes.mode.previousValue; return changes
&& changes.mode
&& changes.mode.currentValue !== changes.mode.previousValue;
} }
private isValidationChanged(changes): boolean { private isValidationChanged(changes: SimpleChanges): boolean {
return changes && changes.validate && changes.validate.currentValue !== changes.validate.previousValue; return changes
&& changes.validate
&& changes.validate.currentValue !== changes.validate.previousValue;
} }
private hasPreselectedGroupsChanged(changes): boolean { private hasPreselectedGroupsChanged(changes: SimpleChanges): boolean {
return changes && changes.preSelectGroups && changes.preSelectGroups.currentValue !== changes.preSelectGroups.previousValue; return changes
&& changes.preSelectGroups
&& changes.preSelectGroups.currentValue !== changes.preSelectGroups.previousValue;
} }
private hasPreselectedGroupsCleared(changes): boolean { private hasPreselectedGroupsCleared(changes: SimpleChanges): boolean {
return changes && changes.preSelectGroups && changes.preSelectGroups.currentValue.length === 0; return changes
} && changes.preSelectGroups
&& changes.preSelectGroups.currentValue.length === 0;
private createSearchParam(value: string): IdentityGroupSearchParam {
const queryParams: IdentityGroupSearchParam = { name: value.trim() };
return queryParams;
}
getSelectedGroups(): IdentityGroupModel[] {
return this.selectedGroups;
} }
private hasRoles(): boolean { private hasRoles(): boolean {
return this.roles && this.roles.length > 0; return this.roles && this.roles.length > 0;
} }
private setTypingError() { private setTypingError(): void {
this.searchGroupsControl.setErrors({ searchTypingError: true, ...this.searchGroupsControl.errors }); this.searchGroupsControl.setErrors({
searchTypingError: true,
...this.searchGroupsControl.errors
});
} }
hasError(): boolean { hasError(): boolean {
@@ -487,8 +475,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
return this.searchGroupsControl.errors.minlength.requiredLength; return this.searchGroupsControl.errors.minlength.requiredLength;
} }
ngOnDestroy() { ngOnDestroy(): void {
clearTimeout(this.currentTimeout);
this.onDestroy$.next(true); this.onDestroy$.next(true);
this.onDestroy$.complete(); this.onDestroy$.complete();
} }

View File

@@ -21,7 +21,8 @@ import {
IdentityUserService, IdentityUserService,
AlfrescoApiService, AlfrescoApiService,
CoreModule, CoreModule,
setupTestBed setupTestBed,
IdentityUserModel
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { of } from 'rxjs'; import { of } from 'rxjs';
@@ -49,33 +50,34 @@ describe('PeopleCloudComponent', () => {
{ id: mockUsers[2].id, username: mockUsers[2].username } { id: mockUsers[2].id, username: mockUsers[2].username }
]; ];
function getElement<T = HTMLElement>(selector: string): T {
return <T> fixture.nativeElement.querySelector(selector);
}
setupTestBed({ setupTestBed({
imports: [ imports: [
CoreModule.forRoot(), CoreModule.forRoot(),
ProcessServiceCloudTestingModule, ProcessServiceCloudTestingModule,
PeopleCloudModule PeopleCloudModule
],
providers: [
IdentityUserService
] ]
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(PeopleCloudComponent); fixture = TestBed.createComponent(PeopleCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
identityService = TestBed.get(IdentityUserService); identityService = TestBed.get(IdentityUserService);
alfrescoApiService = TestBed.get(AlfrescoApiService); alfrescoApiService = TestBed.get(AlfrescoApiService);
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
});
it('should create PeopleCloudComponent', () => { spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
expect(component instanceof PeopleCloudComponent).toBe(true, 'should create PeopleCloudComponent');
}); });
it('should populate placeholder when title is present', async(() => { it('should populate placeholder when title is present', async(() => {
component.title = 'TITLE_KEY'; component.title = 'TITLE_KEY';
fixture.detectChanges(); fixture.detectChanges();
const matLabel: HTMLInputElement = <HTMLInputElement> fixture.nativeElement.querySelector('#adf-people-cloud-title-id');
const matLabel = getElement<HTMLInputElement>('#adf-people-cloud-title-id');
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(matLabel.textContent).toEqual('TITLE_KEY'); expect(matLabel.textContent).toEqual('TITLE_KEY');
@@ -84,7 +86,9 @@ describe('PeopleCloudComponent', () => {
it('should not populate placeholder when title is not present', async(() => { it('should not populate placeholder when title is not present', async(() => {
fixture.detectChanges(); fixture.detectChanges();
const matLabel: HTMLInputElement = <HTMLInputElement> fixture.nativeElement.querySelector('#adf-people-cloud-title-id');
const matLabel = getElement<HTMLInputElement>('#adf-people-cloud-title-id');
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -93,7 +97,6 @@ describe('PeopleCloudComponent', () => {
})); }));
describe('Search user', () => { describe('Search user', () => {
beforeEach(async(() => { beforeEach(async(() => {
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement; element = fixture.nativeElement;
@@ -101,13 +104,13 @@ describe('PeopleCloudComponent', () => {
})); }));
it('should list the users as dropdown options if the search term has results', (done) => { it('should list the users as dropdown options if the search term has results', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'first'; input.value = 'first';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges();
fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3); expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3);
@@ -178,11 +181,13 @@ describe('PeopleCloudComponent', () => {
it('should hide result list if input is empty', (done) => { it('should hide result list if input is empty', (done) => {
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = ''; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = '';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(element.querySelector('mat-option')).toBeNull(); expect(element.querySelector('mat-option')).toBeNull();
@@ -190,11 +195,12 @@ describe('PeopleCloudComponent', () => {
}); });
}); });
it('should selectedUser and changedUsers emit, update selected users when a user is selected', (done) => { it('should update selected users when a user is selected', (done) => {
const user = { username: 'username' }; const user = { username: 'username' };
fixture.detectChanges(); fixture.detectChanges();
const selectEmitSpy = spyOn(component.selectUser, 'emit'); const selectEmitSpy = spyOn(component.selectUser, 'emit');
const changedUsersSpy = spyOn(component.changedUsers, 'emit'); const changedUsersSpy = spyOn(component.changedUsers, 'emit');
component.onSelect(user); component.onSelect(user);
fixture.detectChanges(); fixture.detectChanges();
@@ -206,17 +212,58 @@ describe('PeopleCloudComponent', () => {
}); });
}); });
it('should replace the user in single-selection mode', () => {
component.mode = 'single';
const user1: IdentityUserModel = { id: '1', username: 'user1', email: 'user1@mail.com' };
const user2: IdentityUserModel = { id: '2', username: 'user2', email: 'user2@mail.com' };
component.onSelect(user1);
expect(component.getSelectedUsers()).toEqual([user1]);
component.onSelect(user2);
expect(component.getSelectedUsers()).toEqual([user2]);
});
it('should allow multiple users in multi-selection mode', () => {
component.mode = 'multiple';
const user1: IdentityUserModel = { id: '1', username: 'user1', email: 'user1@mail.com' };
const user2: IdentityUserModel = { id: '2', username: 'user2', email: 'user2@mail.com' };
component.onSelect(user1);
component.onSelect(user2);
expect(component.getSelectedUsers()).toEqual([user1, user2]);
});
it('should allow only unique users in multi-selection mode', () => {
component.mode = 'multiple';
const user1: IdentityUserModel = { id: '1', username: 'user1', email: 'user1@mail.com' };
const user2: IdentityUserModel = { id: '2', username: 'user2', email: 'user2@mail.com' };
component.onSelect(user1);
component.onSelect(user2);
component.onSelect(user1);
component.onSelect(user2);
expect(component.getSelectedUsers()).toEqual([user1, user2]);
});
it('should show an error message if the search result empty', (done) => { it('should show an error message if the search result empty', (done) => {
findUsersByNameSpy.and.returnValue(of([])); findUsersByNameSpy.and.returnValue(of([]));
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'ZZZ'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'ZZZ';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
inputHTMLElement.blur(); input.blur();
fixture.detectChanges(); fixture.detectChanges();
const errorMessage = element.querySelector('[data-automation-id="invalid-users-typing-error"]'); const errorMessage = element.querySelector('[data-automation-id="invalid-users-typing-error"]');
expect(errorMessage).not.toBeNull(); expect(errorMessage).not.toBeNull();
@@ -227,7 +274,6 @@ describe('PeopleCloudComponent', () => {
}); });
describe('when application name defined', () => { describe('when application name defined', () => {
let checkUserHasAccessSpy: jasmine.Spy; let checkUserHasAccessSpy: jasmine.Spy;
let checkUserHasAnyClientAppRoleSpy: jasmine.Spy; let checkUserHasAnyClientAppRoleSpy: jasmine.Spy;
@@ -235,6 +281,7 @@ describe('PeopleCloudComponent', () => {
findUsersByNameSpy = spyOn(identityService, 'findUsersByName').and.returnValue(of(mockUsers)); findUsersByNameSpy = spyOn(identityService, 'findUsersByName').and.returnValue(of(mockUsers));
checkUserHasAccessSpy = spyOn(identityService, 'checkUserHasClientApp').and.returnValue(of(true)); checkUserHasAccessSpy = spyOn(identityService, 'checkUserHasClientApp').and.returnValue(of(true));
checkUserHasAnyClientAppRoleSpy = spyOn(identityService, 'checkUserHasAnyClientAppRole').and.returnValue(of(true)); checkUserHasAnyClientAppRoleSpy = spyOn(identityService, 'checkUserHasAnyClientAppRole').and.returnValue(of(true));
component.preSelectUsers = []; component.preSelectUsers = [];
component.appName = 'mock-app-name'; component.appName = 'mock-app-name';
fixture.detectChanges(); fixture.detectChanges();
@@ -244,8 +291,10 @@ describe('PeopleCloudComponent', () => {
it('should fetch the client ID if appName specified', async (() => { it('should fetch the client ID if appName specified', async (() => {
const getClientIdByApplicationNameSpy = spyOn(identityService, 'getClientIdByApplicationName').and.callThrough(); const getClientIdByApplicationNameSpy = spyOn(identityService, 'getClientIdByApplicationName').and.callThrough();
component.appName = 'mock-app-name'; component.appName = 'mock-app-name';
const change = new SimpleChange(null, 'mock-app-name', false); const change = new SimpleChange(null, 'mock-app-name', false);
component.ngOnChanges({ 'appName': change }); component.ngOnChanges({ 'appName': change });
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -254,11 +303,12 @@ describe('PeopleCloudComponent', () => {
})); }));
it('should list users who have access to the app when appName is specified', (done) => { it('should list users who have access to the app when appName is specified', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'M'; input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -270,11 +320,13 @@ describe('PeopleCloudComponent', () => {
it('should not list users who do not have access to the app when appName is specified', (done) => { it('should not list users who do not have access to the app when appName is specified', (done) => {
checkUserHasAccessSpy.and.returnValue(of(false)); checkUserHasAccessSpy.and.returnValue(of(false));
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -285,11 +337,13 @@ describe('PeopleCloudComponent', () => {
it('should list users if given roles mapped with client roles', (done) => { it('should list users if given roles mapped with client roles', (done) => {
component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1']; component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1'];
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -302,11 +356,13 @@ describe('PeopleCloudComponent', () => {
it('should not list users if roles are not mapping with client roles', (done) => { it('should not list users if roles are not mapping with client roles', (done) => {
checkUserHasAnyClientAppRoleSpy.and.returnValue(of(false)); checkUserHasAnyClientAppRoleSpy.and.returnValue(of(false));
component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1']; component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1'];
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -317,11 +373,12 @@ describe('PeopleCloudComponent', () => {
}); });
it('should not call client role mapping sevice if roles not specified', (done) => { it('should not call client role mapping sevice if roles not specified', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'M'; input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -331,11 +388,12 @@ describe('PeopleCloudComponent', () => {
}); });
it('should validate access to the app when appName is specified', (done) => { it('should validate access to the app when appName is specified', (done) => {
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input'); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.focus(); input.focus();
inputHTMLElement.value = 'M'; input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('keyup')); input.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -347,11 +405,13 @@ describe('PeopleCloudComponent', () => {
it('should not validate access to the app when appName is not specified', (done) => { it('should not validate access to the app when appName is not specified', (done) => {
component.appName = ''; component.appName = '';
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'M';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -364,15 +424,18 @@ describe('PeopleCloudComponent', () => {
checkUserHasAccessSpy.and.returnValue(of(false)); checkUserHasAccessSpy.and.returnValue(of(false));
findUsersByNameSpy.and.returnValue(of([])); findUsersByNameSpy.and.returnValue(of([]));
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'ZZZ'; input.focus();
inputHTMLElement.dispatchEvent(new Event('keyup')); input.value = 'ZZZ';
inputHTMLElement.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
inputHTMLElement.blur(); input.blur();
fixture.detectChanges(); fixture.detectChanges();
const errorMessage = element.querySelector('[data-automation-id="invalid-users-typing-error"]'); const errorMessage = element.querySelector('[data-automation-id="invalid-users-typing-error"]');
expect(errorMessage).not.toBeNull(); expect(errorMessage).not.toBeNull();
expect(errorMessage.textContent).toContain('ADF_CLOUD_USERS.ERROR.NOT_FOUND'); expect(errorMessage.textContent).toContain('ADF_CLOUD_USERS.ERROR.NOT_FOUND');
@@ -402,7 +465,6 @@ describe('PeopleCloudComponent', () => {
}); });
describe('When roles defined', () => { describe('When roles defined', () => {
let checkUserHasRoleSpy: jasmine.Spy; let checkUserHasRoleSpy: jasmine.Spy;
beforeEach(async(() => { beforeEach(async(() => {
@@ -415,10 +477,12 @@ describe('PeopleCloudComponent', () => {
it('should filter users if users has any specified role', (done) => { it('should filter users if users has any specified role', (done) => {
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('input')); input.value = 'M';
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -431,10 +495,12 @@ describe('PeopleCloudComponent', () => {
it('should not filter users if user does not have any specified role', (done) => { it('should not filter users if user does not have any specified role', (done) => {
fixture.detectChanges(); fixture.detectChanges();
checkUserHasRoleSpy.and.returnValue(of(false)); checkUserHasRoleSpy.and.returnValue(of(false));
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('input')); input.value = 'M';
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -447,10 +513,12 @@ describe('PeopleCloudComponent', () => {
it('should not call checkUserHasRole service when roles are not specified', (done) => { it('should not call checkUserHasRole service when roles are not specified', (done) => {
component.roles = []; component.roles = [];
fixture.detectChanges(); fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus(); const input = getElement<HTMLInputElement>('input');
inputHTMLElement.value = 'M'; input.focus();
inputHTMLElement.dispatchEvent(new Event('input')); input.value = 'M';
input.dispatchEvent(new Event('input'));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@@ -461,13 +529,13 @@ describe('PeopleCloudComponent', () => {
}); });
describe('Single Mode with Pre-selected users', () => { describe('Single Mode with Pre-selected users', () => {
const changes = new SimpleChange(null, mockPreselectedUsers, false); const changes = new SimpleChange(null, mockPreselectedUsers, false);
beforeEach(async(() => { beforeEach(async(() => {
component.mode = 'single'; component.mode = 'single';
component.preSelectUsers = <any> mockPreselectedUsers; component.preSelectUsers = <any> mockPreselectedUsers;
component.ngOnChanges({ 'preSelectUsers': changes }); component.ngOnChanges({ 'preSelectUsers': changes });
fixture.detectChanges(); fixture.detectChanges();
element = fixture.nativeElement; element = fixture.nativeElement;
})); }));
@@ -484,53 +552,85 @@ describe('PeopleCloudComponent', () => {
}); });
describe('Multiple Mode with Pre-selected Users', () => { describe('Multiple Mode with Pre-selected Users', () => {
beforeEach(() => {
component.mode = 'multiple';
});
it('should render multiple preselected users', (done) => {
fixture.detectChanges();
const changes = new SimpleChange(null, mockPreselectedUsers, false);
component.preSelectUsers = <any> mockPreselectedUsers;
component.ngOnChanges({ 'preSelectUsers': changes });
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
expect(chips.length).toEqual(2);
expect(chips[0].attributes['data-automation-id']).toEqual(`adf-people-cloud-chip-${mockPreselectedUsers[0].username}`);
expect(chips[1].attributes['data-automation-id']).toEqual(`adf-people-cloud-chip-${mockPreselectedUsers[1].username}`);
done();
});
});
it('Should not show remove icon for pre-selected users if readonly property set to true', (done) => { it('Should not show remove icon for pre-selected users if readonly property set to true', (done) => {
fixture.detectChanges(); fixture.detectChanges();
component.preSelectUsers = [ component.preSelectUsers = [
{ id: mockUsers[0].id, username: mockUsers[0].username, readonly: true }, { id: mockUsers[0].id, username: mockUsers[0].username, readonly: true },
{ id: mockUsers[1].id, username: mockUsers[1].username, readonly: true } { id: mockUsers[1].id, username: mockUsers[1].username, readonly: true }
]; ];
const change = new SimpleChange(null, component.preSelectUsers, false);
component.mode = 'multiple';
component.ngOnChanges({ 'preSelectUsers': change });
fixture.detectChanges();
const change = new SimpleChange(null, component.preSelectUsers, false);
component.ngOnChanges({ 'preSelectUsers': change });
fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip'); const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
const removeIcon = <HTMLElement> fixture.nativeElement.querySelector('[data-automation-id="adf-people-cloud-chip-remove-icon-first-name-1 last-name-1"]'); const removeIcon = getElement('[data-automation-id="adf-people-cloud-chip-remove-icon-first-name-1 last-name-1"]');
expect(chipList.length).toBe(2); expect(chipList.length).toBe(2);
expect(component.preSelectUsers[0].readonly).toBeTruthy(); expect(component.preSelectUsers[0].readonly).toBeTruthy();
expect(component.preSelectUsers[1].readonly).toBeTruthy(); expect(component.preSelectUsers[1].readonly).toBeTruthy();
expect(removeIcon).toBeNull(); expect(removeIcon).toBeNull();
done(); done();
}); });
}); });
it('Should be able to remove preselected users if readonly property set to false', (done) => { it('Should be able to remove preselected users if readonly property set to false', (done) => {
fixture.detectChanges();
component.preSelectUsers = [ component.preSelectUsers = [
{ id: mockUsers[0].id, username: mockUsers[0].username, readonly: false }, { id: mockUsers[0].id, username: mockUsers[0].username, readonly: false },
{ id: mockUsers[1].id, username: mockUsers[1].username, readonly: false } { id: mockUsers[1].id, username: mockUsers[1].username, readonly: false }
]; ];
const change = new SimpleChange(null, component.preSelectUsers, false);
component.mode = 'multiple';
component.ngOnChanges({ 'preSelectUsers': change });
const removeUserSpy = spyOn(component.removeUser, 'emit');
fixture.detectChanges();
const change = new SimpleChange(null, component.preSelectUsers, false);
component.ngOnChanges({ 'preSelectUsers': change });
const removeUserSpy = spyOn(component.removeUser, 'emit');
fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const removeIcon = <HTMLElement> fixture.nativeElement.querySelector(`[data-automation-id="adf-people-cloud-chip-remove-icon-${mockPreselectedUsers[0].username}"]`); const removeIcon = getElement(`[data-automation-id="adf-people-cloud-chip-remove-icon-${mockPreselectedUsers[0].username}"]`);
expect(chips.length).toBe(2); expect(chips.length).toBe(2);
expect(component.preSelectUsers[0].readonly).toBe(false, 'Removable'); expect(component.preSelectUsers[0].readonly).toBe(false, 'Removable');
expect(component.preSelectUsers[1].readonly).toBe(false, 'Removable'); expect(component.preSelectUsers[1].readonly).toBe(false, 'Removable');
removeIcon.click(); removeIcon.click();
fixture.detectChanges(); fixture.detectChanges();
expect(removeUserSpy).toHaveBeenCalled(); expect(removeUserSpy).toHaveBeenCalled();
expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1); expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1);
done(); done();
}); });
}); });
@@ -545,8 +645,10 @@ describe('PeopleCloudComponent', () => {
component.ngOnChanges({ 'preSelectUsers': change }); component.ngOnChanges({ 'preSelectUsers': change });
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chipList = fixture.nativeElement.querySelector('mat-chip-list'); const chipList = getElement('mat-chip-list');
expect(chips).toBeDefined(); expect(chips).toBeDefined();
expect(chipList).toBeDefined(); expect(chipList).toBeDefined();
expect(chips.length).toBe(1); expect(chips.length).toBe(1);
@@ -558,9 +660,12 @@ describe('PeopleCloudComponent', () => {
component.readOnly = true; component.readOnly = true;
component.preSelectUsers = <any> mockPreselectedUsers; component.preSelectUsers = <any> mockPreselectedUsers;
component.ngOnChanges({ 'preSelectUsers': change }); component.ngOnChanges({ 'preSelectUsers': change });
fixture.detectChanges(); fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip')); const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chipList = fixture.nativeElement.querySelector('mat-chip-list'); const chipList = getElement('mat-chip-list');
expect(chips).toBeDefined(); expect(chips).toBeDefined();
expect(chipList).toBeDefined(); expect(chipList).toBeDefined();
expect(chips.length).toBe(2); expect(chips.length).toBe(2);
@@ -588,7 +693,26 @@ describe('PeopleCloudComponent', () => {
component.mode = 'single'; component.mode = 'single';
component.validate = true; component.validate = true;
component.preSelectUsers = <any> [mockPreselectedUsers[0], mockPreselectedUsers[1]]; component.preSelectUsers = <any> [mockPreselectedUsers[0], mockPreselectedUsers[1]];
component.ngOnChanges({ 'preSelectUsers': new SimpleChange(null, [mockPreselectedUsers[0], mockPreselectedUsers[1]], false) }); component.ngOnChanges({
'preSelectUsers': new SimpleChange(null, [mockPreselectedUsers[0], mockPreselectedUsers[1]], false)
});
});
it('should skip warnings if validation disabled', () => {
spyOn(identityService, 'findUserById').and.returnValue(Promise.resolve([]));
spyOn(component, 'compare').and.returnValue(false);
let warnings = 0;
component.warning.subscribe(() => warnings++);
component.mode = 'single';
component.validate = false;
component.preSelectUsers = <any> [mockPreselectedUsers[0], mockPreselectedUsers[1]];
component.ngOnChanges({
'preSelectUsers': new SimpleChange(null, [mockPreselectedUsers[0], mockPreselectedUsers[1]], false)
});
expect(warnings).toBe(0);
}); });
it('should check validation for all the users and emit warning - multiple mode', (done) => { it('should check validation for all the users and emit warning - multiple mode', (done) => {
@@ -604,7 +728,8 @@ describe('PeopleCloudComponent', () => {
{ {
id: mockPreselectedUsers[1].id, id: mockPreselectedUsers[1].id,
username: mockPreselectedUsers[1].username username: mockPreselectedUsers[1].username
}] }
]
}; };
component.warning.subscribe(warning => { component.warning.subscribe(warning => {
@@ -615,7 +740,9 @@ describe('PeopleCloudComponent', () => {
component.mode = 'multiple'; component.mode = 'multiple';
component.validate = true; component.validate = true;
component.preSelectUsers = <any> [mockPreselectedUsers[0], mockPreselectedUsers[1]]; component.preSelectUsers = <any> [mockPreselectedUsers[0], mockPreselectedUsers[1]];
component.ngOnChanges({ 'preSelectUsers': new SimpleChange(null, [mockPreselectedUsers[0], mockPreselectedUsers[1]], false) }); component.ngOnChanges({
'preSelectUsers': new SimpleChange(null, [mockPreselectedUsers[0], mockPreselectedUsers[1]], false)
});
}); });
}); });

View File

@@ -27,7 +27,7 @@ import {
OnChanges, OnChanges,
OnDestroy, OnDestroy,
ChangeDetectionStrategy, ChangeDetectionStrategy,
ViewChild, ElementRef ViewChild, ElementRef, SimpleChange
} from '@angular/core'; } from '@angular/core';
import { Observable, of, BehaviorSubject, Subject } from 'rxjs'; import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, map, takeUntil } from 'rxjs/operators'; import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, map, takeUntil } from 'rxjs/operators';
@@ -38,6 +38,7 @@ import {
LogService LogService
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { trigger, state, style, transition, animate } from '@angular/animations'; import { trigger, state, style, transition, animate } from '@angular/animations';
import { ComponentSelectionMode } from '../../types';
@Component({ @Component({
selector: 'adf-cloud-people', selector: 'adf-cloud-people',
@@ -59,16 +60,13 @@ import { trigger, state, style, transition, animate } from '@angular/animations'
export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy { export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
static MODE_SINGLE = 'single';
static MODE_MULTIPLE = 'multiple';
/** Name of the application. If specified, this shows the users who have access to the app. */ /** Name of the application. If specified, this shows the users who have access to the app. */
@Input() @Input()
appName: string; appName: string;
/** User selection mode (single/multiple). */ /** User selection mode (single/multiple). */
@Input() @Input()
mode: string = PeopleCloudComponent.MODE_SINGLE; mode: ComponentSelectionMode = 'single';
/** Role names of the users to be listed. */ /** Role names of the users to be listed. */
@Input() @Input()
@@ -79,7 +77,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
* Otherwise, no check will be done. * Otherwise, no check will be done.
*/ */
@Input() @Input()
validate: Boolean = false; validate: boolean = false;
/** Show the info in readonly mode /** Show the info in readonly mode
*/ */
@@ -96,7 +94,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
/** FormControl to search the user */ /** FormControl to search the user */
@Input() @Input()
searchUserCtrl: FormControl = new FormControl({ value: '', disabled: false }); searchUserCtrl = new FormControl({ value: '', disabled: false });
/** Placeholder translation key /** Placeholder translation key
*/ */
@@ -123,18 +121,16 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
private userInput: ElementRef<HTMLInputElement>; private userInput: ElementRef<HTMLInputElement>;
private _searchUsers: IdentityUserModel[] = []; private _searchUsers: IdentityUserModel[] = [];
private searchUsersSubject: BehaviorSubject<IdentityUserModel[]>;
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
selectedUsers: IdentityUserModel[] = []; selectedUsers: IdentityUserModel[] = [];
invalidUsers: IdentityUserModel[] = []; invalidUsers: IdentityUserModel[] = [];
searchUsers$: Observable<IdentityUserModel[]>; searchUsers$ = new BehaviorSubject<IdentityUserModel[]>(this._searchUsers);
_subscriptAnimationState: string = 'enter'; _subscriptAnimationState: string = 'enter';
clientId: string; clientId: string;
isFocused: boolean; isFocused: boolean;
currentTimeout: any;
validateUsersMessage: string; validateUsersMessage: string;
searchedValue = ''; searchedValue = '';
@@ -144,19 +140,17 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
private identityUserService: IdentityUserService, private identityUserService: IdentityUserService,
private logService: LogService) {} private logService: LogService) {}
ngOnInit() { ngOnInit(): void {
if (this.searchUsersSubject === undefined) {
this.searchUsersSubject = new BehaviorSubject<IdentityUserModel[]>(this._searchUsers);
this.searchUsers$ = this.searchUsersSubject.asObservable();
}
this.loadClientId(); this.loadClientId();
this.initSearch(); this.initSearch();
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges): void {
if (this.hasPreselectedUsersChanged(changes) || this.hasModeChanged(changes) || this.isValidationChanged(changes)) { if (this.valueChanged(changes.preSelectUsers)
|| this.valueChanged(changes.mode)
|| this.valueChanged(changes.validate)
) {
if (this.hasPreSelectUsers()) { if (this.hasPreSelectUsers()) {
this.loadPreSelectUsers(); this.loadPreSelectUsers();
} else if (this.hasPreselectedUsersCleared(changes)) { } else if (this.hasPreselectedUsersCleared(changes)) {
@@ -175,14 +169,14 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
private async loadClientId() { private async loadClientId(): Promise<void> {
this.clientId = await this.identityUserService.getClientIdByApplicationName(this.appName).toPromise(); this.clientId = await this.identityUserService.getClientIdByApplicationName(this.appName).toPromise();
if (this.clientId) { if (this.clientId) {
this.searchUserCtrl.enable(); this.searchUserCtrl.enable();
} }
} }
private initSearch() { private initSearch(): void {
this.searchUserCtrl.valueChanges.pipe( this.searchUserCtrl.valueChanges.pipe(
debounceTime(500), debounceTime(500),
distinctUntilChanged(), distinctUntilChanged(),
@@ -207,15 +201,13 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
this.resetSearchUsers(); this.resetSearchUsers();
return users; return users;
}), }),
filter((user: any) => { filter(user => !this.isUserAlreadySelected(user)),
return !this.isUserAlreadySelected(user); mergeMap(user => {
}),
mergeMap((user: any) => {
if (this.appName) { if (this.appName) {
return this.checkUserHasAccess(user.id).pipe( return this.checkUserHasAccess(user.id).pipe(
mergeMap((hasRole) => { mergeMap(
return hasRole ? of(user) : of(); hasRole => hasRole ? of(user) : of()
}) )
); );
} else if (this.hasRoles()) { } else if (this.hasRoles()) {
return this.filterUsersByRoles(user); return this.filterUsersByRoles(user);
@@ -224,19 +216,18 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
}), }),
takeUntil(this.onDestroy$) takeUntil(this.onDestroy$)
).subscribe((user: any) => { ).subscribe(user => {
this._searchUsers.push(user); this._searchUsers.push(user);
this.searchUsersSubject.next(this._searchUsers); this.searchUsers$.next(this._searchUsers);
}); });
} }
ngOnDestroy() { ngOnDestroy(): void {
clearTimeout(this.currentTimeout);
this.onDestroy$.next(true); this.onDestroy$.next(true);
this.onDestroy$.complete(); this.onDestroy$.complete();
} }
private isAppNameChanged(change): boolean { private isAppNameChanged(change: SimpleChange): boolean {
return change && change.previousValue !== change.currentValue && this.appName && this.appName.length > 0; return change && change.previousValue !== change.currentValue && this.appName && this.appName.length > 0;
} }
@@ -274,7 +265,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
return false; return false;
} }
private async loadPreSelectUsers() { private async loadPreSelectUsers(): Promise<void> {
this.selectedUsers = []; this.selectedUsers = [];
if (this.isSingleMode()) { if (this.isSingleMode()) {
@@ -286,6 +277,15 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
if (this.isValidationEnabled()) { if (this.isValidationEnabled()) {
this.isLoading = true; this.isLoading = true;
await this.validatePreselectUsers(); await this.validatePreselectUsers();
this.isLoading = false;
}
}
private getPreselectedUsers(): IdentityUserModel[] {
if (this.isSingleMode()) {
return [this.preSelectUsers[0]];
} else {
return this.removeDuplicatedUsers(this.preSelectUsers);
} }
} }
@@ -293,31 +293,24 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
this.invalidUsers = []; this.invalidUsers = [];
const validUsers: IdentityUserModel[] = []; const validUsers: IdentityUserModel[] = [];
let preselectedUsersToValidate: IdentityUserModel[] = []; for (const user of this.getPreselectedUsers()) {
if (this.isSingleMode()) {
preselectedUsersToValidate = [this.preSelectUsers[0]];
} else {
preselectedUsersToValidate = this.removeDuplicatedUsers(this.preSelectUsers);
}
await Promise.all(preselectedUsersToValidate.map(async (preselectedUser: IdentityUserModel) => {
try { try {
const userValidationResult: IdentityUserModel = await this.searchUser(preselectedUser); const validationResult = await this.searchUser(user);
if (this.compare(preselectedUser, userValidationResult)) {
userValidationResult.readonly = preselectedUser.readonly; if (this.compare(user, validationResult)) {
validUsers.push(userValidationResult); validationResult.readonly = user.readonly;
validUsers.push(validationResult);
} else { } else {
this.invalidUsers.push(preselectedUser); this.invalidUsers.push(user);
} }
} catch (error) { } catch (error) {
this.invalidUsers.push(preselectedUser); this.invalidUsers.push(user);
this.logService.error(error); this.logService.error(error);
} }
})); }
this.checkPreselectValidationErrors(); this.checkPreselectValidationErrors();
this.selectedUsers = validUsers.concat(this.invalidUsers); this.selectedUsers = validUsers.concat(this.invalidUsers);
this.isLoading = false;
} }
compare(preselectedUser: IdentityUserModel, identityUser: IdentityUserModel): boolean { compare(preselectedUser: IdentityUserModel, identityUser: IdentityUserModel): boolean {
@@ -332,16 +325,20 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
return false; return false;
} }
async searchUser(user: IdentityUserModel) { private getSearchKey(user: IdentityUserModel): string {
let key: string = '';
if (user.id) { if (user.id) {
key = 'id'; return 'id';
} else if (user.email) { } else if (user.email) {
key = 'email'; return 'email';
} else if (user.username) { } else if (user.username) {
key = 'username'; return 'username';
} else {
return null;
} }
}
async searchUser(user: IdentityUserModel): Promise<IdentityUserModel> {
const key = this.getSearchKey(user);
switch (key) { switch (key) {
case 'id': case 'id':
@@ -351,19 +348,18 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
case 'email': case 'email':
return (await this.identityUserService.findUserByEmail(user[key]).toPromise())[0]; return (await this.identityUserService.findUserByEmail(user[key]).toPromise())[0];
default: default:
return of([]); return null;
} }
} }
removeDuplicatedUsers(users: IdentityUserModel[]): IdentityUserModel[] { removeDuplicatedUsers(users: IdentityUserModel[]): IdentityUserModel[] {
return users.filter((user, index, self) => return users.filter((user, index, self) =>
index === self.findIndex((auxUser) => { index === self.findIndex(auxUser =>
return user.id === auxUser.id && user.username === auxUser.username && user.email === auxUser.email; user.id === auxUser.id && user.username === auxUser.username && user.email === auxUser.email
})); ));
} }
public checkPreselectValidationErrors() { checkPreselectValidationErrors(): void {
this.invalidUsers = this.removeDuplicatedUsers(this.invalidUsers); this.invalidUsers = this.removeDuplicatedUsers(this.invalidUsers);
if (this.invalidUsers.length > 0) { if (this.invalidUsers.length > 0) {
@@ -376,8 +372,9 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
onSelect(user: IdentityUserModel) { onSelect(user: IdentityUserModel): void {
this.selectUser.emit(user); this.selectUser.emit(user);
if (this.isMultipleMode()) { if (this.isMultipleMode()) {
if (!this.isUserAlreadySelected(user)) { if (!this.isUserAlreadySelected(user)) {
this.selectedUsers.push(user); this.selectedUsers.push(user);
@@ -394,7 +391,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
this.resetSearchUsers(); this.resetSearchUsers();
} }
onRemove(userToRemove: IdentityUserModel) { onRemove(userToRemove: IdentityUserModel): void {
this.removeUser.emit(userToRemove); this.removeUser.emit(userToRemove);
this.removeUserFromSelected(userToRemove); this.removeUserFromSelected(userToRemove);
this.changedUsers.emit(this.selectedUsers); this.changedUsers.emit(this.selectedUsers);
@@ -406,18 +403,23 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
private removeUserFromSelected(userToRemove: IdentityUserModel) { private removeUserFromSelected({ id, username, email }: IdentityUserModel): void {
const indexToRemove = this.selectedUsers.findIndex((selectedUser: IdentityUserModel) => { const indexToRemove = this.selectedUsers.findIndex(user => {
return selectedUser.id === userToRemove.id && selectedUser.username === userToRemove.username && selectedUser.email === userToRemove.email; return user.id === id
&& user.username === username
&& user.email === email;
}); });
if (indexToRemove !== -1) { if (indexToRemove !== -1) {
this.selectedUsers.splice(indexToRemove, 1); this.selectedUsers.splice(indexToRemove, 1);
} }
} }
private removeUserFromValidation(userToRemove: IdentityUserModel) { private removeUserFromValidation({ id, username, email }: IdentityUserModel): void {
const indexToRemove = this.invalidUsers.findIndex((invalidUser) => { const indexToRemove = this.invalidUsers.findIndex(user => {
return invalidUser.username === userToRemove.username && invalidUser.id === userToRemove.id && invalidUser.email === userToRemove.email; return user.id === id
&& user.username === username
&& user.email === email;
}); });
if (indexToRemove !== -1) { if (indexToRemove !== -1) {
@@ -425,10 +427,10 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
generateInvalidUsersMessage() { generateInvalidUsersMessage(): void {
this.validateUsersMessage = ''; this.validateUsersMessage = '';
this.invalidUsers.forEach((invalidUser: IdentityUserModel, index) => { this.invalidUsers.forEach((invalidUser, index) => {
if (index === this.invalidUsers.length - 1) { if (index === this.invalidUsers.length - 1) {
this.validateUsersMessage += `${invalidUser.username} `; this.validateUsersMessage += `${invalidUser.username} `;
} else { } else {
@@ -437,12 +439,16 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
setTypingError() { setTypingError(): void {
this.searchUserCtrl.setErrors({ searchTypingError: true, ...this.searchUserCtrl.errors }); this.searchUserCtrl.setErrors({
searchTypingError: true,
...this.searchUserCtrl.errors
});
} }
hasPreselectError(): boolean { hasPreselectError(): boolean {
return this.invalidUsers && this.invalidUsers.length > 0; return this.invalidUsers
&& this.invalidUsers.length > 0;
} }
getDisplayName(user): string { getDisplayName(user): string {
@@ -450,40 +456,39 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
isMultipleMode(): boolean { isMultipleMode(): boolean {
return this.mode === PeopleCloudComponent.MODE_MULTIPLE; return this.mode === 'multiple';
} }
isSingleMode(): boolean { isSingleMode(): boolean {
return this.mode === PeopleCloudComponent.MODE_SINGLE; return this.mode === 'single';
} }
private isSingleSelectionReadonly(): boolean { private isSingleSelectionReadonly(): boolean {
return this.isSingleMode() && this.selectedUsers.length === 1 && this.selectedUsers[0].readonly === true; return this.isSingleMode()
&& this.selectedUsers.length === 1
&& this.selectedUsers[0].readonly === true;
} }
private hasPreSelectUsers(): boolean { private hasPreSelectUsers(): boolean {
return this.preSelectUsers && this.preSelectUsers.length > 0; return this.preSelectUsers
&& this.preSelectUsers.length > 0;
} }
private hasModeChanged(changes): boolean { private valueChanged(change: SimpleChange): boolean {
return changes && changes.mode && changes.mode.currentValue !== changes.mode.previousValue; return change
&& change.currentValue !== change.previousValue;
} }
private isValidationChanged(changes): boolean { private hasPreselectedUsersCleared(changes: SimpleChanges): boolean {
return changes && changes.validate && changes.validate.currentValue !== changes.validate.previousValue; return changes
&& changes.preSelectUsers
&& changes.preSelectUsers.currentValue
&& changes.preSelectUsers.currentValue.length === 0;
} }
private hasPreselectedUsersChanged(changes): boolean { private resetSearchUsers(): void {
return changes && changes.preSelectUsers && changes.preSelectUsers.currentValue !== changes.preSelectUsers.previousValue;
}
private hasPreselectedUsersCleared(changes): boolean {
return changes && changes.preSelectUsers && changes.preSelectUsers.currentValue && changes.preSelectUsers.currentValue.length === 0;
}
private resetSearchUsers() {
this._searchUsers = []; this._searchUsers = [];
this.searchUsersSubject.next(this._searchUsers); this.searchUsers$.next(this._searchUsers);
} }
getSelectedUsers(): IdentityUserModel[] { getSelectedUsers(): IdentityUserModel[] {

View File

@@ -0,0 +1,18 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type ComponentSelectionMode = 'single' | 'multiple';

View File

@@ -23,3 +23,4 @@ export * from './lib/group/public-api';
export * from './lib/people/public-api'; export * from './lib/people/public-api';
export * from './lib/form/public-api'; export * from './lib/form/public-api';
export * from './lib/services/public-api'; export * from './lib/services/public-api';
export * from './lib/types';