From cc4c9bc5671f916fb6028848e85304ed722b0318 Mon Sep 17 00:00:00 2001 From: siva kumar Date: Wed, 4 Dec 2019 18:56:56 +0530 Subject: [PATCH] [ADF-5034] [GroupCloudComponent] Provide a way to make pre-selected groups read-only. (#5306) * Added readonly property to the IdentityGroupModel. * Updated unit tests. * Changed class to interface. * Updated doc --- .../components/group-cloud.component.md | 32 + lib/core/mock/identity-group.service.mock.ts | 22 +- lib/core/mock/identity-user.service.mock.ts | 12 +- lib/core/models/identity-group.model.ts | 40 +- .../components/group-cloud.component.html | 6 +- .../components/group-cloud.component.spec.ts | 670 +++++++++++------- .../lib/group/pipe/group-initial.pipe.spec.ts | 2 +- 7 files changed, 493 insertions(+), 291 deletions(-) diff --git a/docs/process-services-cloud/components/group-cloud.component.md b/docs/process-services-cloud/components/group-cloud.component.md index b54169f1a9..d2cb38e0e1 100644 --- a/docs/process-services-cloud/components/group-cloud.component.md +++ b/docs/process-services-cloud/components/group-cloud.component.md @@ -92,3 +92,35 @@ export class MyComponent { [preSelectGroups]="groups"> ``` + +### Read-only + +You can use `readonly` property to make preselected groups read-only in `multiple` mode. + +Usage example: + +```ts +import { ObjectDataTableAdapter } from '@alfresco/adf-core'; + +@Component({...}) +export class MyComponent { + groups: any; + + constructor() { + this.groups = + [ + {id: 1, name: 'Group 1', readonly: true}, + {id: 2, name: 'Group 2', readonly: false} + ]; + } +} +``` + +```html + + +``` + +from above `preSelectGroups`, `Group 2` is removable from the `preSelectGroups` whereas `Group 1` is readonly you can not remove them. diff --git a/lib/core/mock/identity-group.service.mock.ts b/lib/core/mock/identity-group.service.mock.ts index 94e346339d..ce3327d4e1 100644 --- a/lib/core/mock/identity-group.service.mock.ts +++ b/lib/core/mock/identity-group.service.mock.ts @@ -18,30 +18,30 @@ import { IdentityGroupModel, IdentityGroupCountModel } from '../models/identity-group.model'; import { IdentityRoleModel } from '../models/identity-role.model'; -export let mockIdentityGroup1 = new IdentityGroupModel({ +export let mockIdentityGroup1 = { id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [] -}); +}; -export let mockIdentityGroup2 = new IdentityGroupModel({ +export let mockIdentityGroup2 = { id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] -}); +}; -export let mockIdentityGroup3 = new IdentityGroupModel({ +export let mockIdentityGroup3 = { id: 'mock-group-id-3', name: 'Mock Group 3', path: '', subGroups: [] -}); +}; -export let mockIdentityGroup4 = new IdentityGroupModel({ +export let mockIdentityGroup4 = { id: 'mock-group-id-4', name: 'Mock Group 4', path: '', subGroups: [] -}); +}; -export let mockIdentityGroup5 = new IdentityGroupModel({ +export let mockIdentityGroup5 = { id: 'mock-group-id-5', name: 'Mock Group 5', path: '', subGroups: [] -}); +}; export let mockIdentityGroupsCount = { count: 10 }; export let mockIdentityGroups = [ - mockIdentityGroup1, mockIdentityGroup2, mockIdentityGroup3, mockIdentityGroup5, mockIdentityGroup5 + mockIdentityGroup1, mockIdentityGroup2, mockIdentityGroup3, mockIdentityGroup4, mockIdentityGroup5 ]; export let mockApplicationDetails = {id: 'mock-app-id', name: 'mock-app-name'}; diff --git a/lib/core/mock/identity-user.service.mock.ts b/lib/core/mock/identity-user.service.mock.ts index e5f05e4cc7..495d43f58c 100644 --- a/lib/core/mock/identity-user.service.mock.ts +++ b/lib/core/mock/identity-user.service.mock.ts @@ -57,17 +57,17 @@ export const mockEffectiveRoles = [ export const mockJoinGroupRequest: IdentityJoinGroupRequestModel = {userId: 'mock-hser-id', groupId: 'mock-group-id', realm: 'mock-realm-name'}; -export const mockGroup1 = new IdentityGroupModel({ +export const mockGroup1 = { id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [] -}); +}; -export const mockGroup2 = new IdentityGroupModel({ +export const mockGroup2 = { id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] -}); +}; export const mockGroups = [ - new IdentityGroupModel({ id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [] }), - new IdentityGroupModel({ id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] }) + { id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [] }, + { id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] } ]; export const queryUsersMockApi = { diff --git a/lib/core/models/identity-group.model.ts b/lib/core/models/identity-group.model.ts index 7e852440fd..9edefa39b7 100644 --- a/lib/core/models/identity-group.model.ts +++ b/lib/core/models/identity-group.model.ts @@ -17,27 +17,15 @@ import { Pagination } from '@alfresco/js-api'; -export class IdentityGroupModel { - - id: string; - name: string; - path: string; - realmRoles: string[]; - clientRoles: any; - access: any; - attributes: any; - - constructor(obj?: any) { - if (obj) { - this.id = obj.id || null; - this.name = obj.name || null; - this.path = obj.path || null; - this.realmRoles = obj.realmRoles || null; - this.clientRoles = obj.clientRoles || null; - this.access = obj.access || null; - this.attributes = obj.attributes || null; - } - } +export interface IdentityGroupModel { + id?: string; + name?: string; + path?: string; + realmRoles?: string[]; + clientRoles?: any; + access?: any; + attributes?: any; + readonly?: boolean; } export interface IdentityGroupSearchParam { @@ -50,17 +38,9 @@ export interface IdentityGroupQueryResponse { pagination: Pagination; } -export class IdentityGroupQueryCloudRequestModel { - +export interface IdentityGroupQueryCloudRequestModel { first: number; max: number; - - constructor(obj?: any) { - if (obj) { - this.first = obj.first; - this.max = obj.max; - } - } } export interface IdentityGroupCountModel { diff --git a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.html b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.html index 5941c3d6dc..a150557977 100644 --- a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.html +++ b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.html @@ -4,10 +4,14 @@ {{group.name}} - cancel + + cancel + { let component: GroupCloudComponent; let fixture: ComponentFixture; let element: HTMLElement; - let service: IdentityGroupService; + let identityGroupService: IdentityGroupService; + let alfrescoApiService: AlfrescoApiService; let findGroupsByNameSpy: jasmine.Spy; - let getClientIdByApplicationNameSpy: jasmine.Spy; - let checkGroupHasAccessSpy: jasmine.Spy; - let checkGroupHasGivenRoleSpy: jasmine.Spy; + + const mock = { + oauth2Auth: { + callCustomApi: () => Promise.resolve(mockIdentityGroups) + } + }; setupTestBed({ - imports: [ProcessServiceCloudTestingModule, GroupCloudModule], - providers: [AlfrescoApiServiceMock, IdentityGroupService] + imports: [ + CoreModule.forRoot(), + ProcessServiceCloudTestingModule, + GroupCloudModule], + providers: [ + IdentityGroupService + ] }); beforeEach(() => { fixture = TestBed.createComponent(GroupCloudComponent); component = fixture.componentInstance; element = fixture.nativeElement; - service = TestBed.get(IdentityGroupService); - findGroupsByNameSpy = spyOn(service, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); - getClientIdByApplicationNameSpy = spyOn(service, 'getClientIdByApplicationName').and.returnValue(of('mock-client-id')); - checkGroupHasAccessSpy = spyOn(service, 'checkGroupHasClientApp').and.returnValue(of(true)); - checkGroupHasGivenRoleSpy = spyOn(service, 'checkGroupHasRole').and.returnValue(of(true)); - component.appName = 'mock-app-name'; + identityGroupService = TestBed.get(IdentityGroupService); + alfrescoApiService = TestBed.get(AlfrescoApiService); + spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock); }); it('should create GroupCloudComponent', () => { expect(component instanceof GroupCloudComponent).toBeTruthy(); }); - it('should be able to fetch client id', async(() => { - component.ngOnChanges( { - appName: new SimpleChange(null, component.appName, true) - }); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(getClientIdByApplicationNameSpy).toHaveBeenCalled(); - expect(component.clientId).toBe('mock-client-id'); - }); - })); + describe('Search group', () => { - it('should show the groups if the typed result match', async(() => { - fixture.detectChanges(); - component.searchGroups$.next( mockIdentityGroups); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.dispatchEvent(new Event('input')); - inputHTMLElement.dispatchEvent(new Event('keyup')); - inputHTMLElement.dispatchEvent(new Event('keydown')); - inputHTMLElement.value = 'M'; - fixture.detectChanges(); - fixture.whenStable().then(() => { + beforeEach(async(() => { fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('mat-option'))).toBeDefined(); - }); - })); + element = fixture.nativeElement; + findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); + })); - it('should hide result list if input is empty', async(() => { - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = ''; - inputHTMLElement.dispatchEvent(new Event('keyup')); - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(fixture.debugElement.query(By.css('mat-option'))).toBeNull(); - expect(fixture.debugElement.query(By.css('#adf-group-0'))).toBeNull(); - }); - })); - - it('should emit selectedGroup if option is valid', async(() => { - fixture.detectChanges(); - const selectEmitSpy = spyOn(component.selectGroup, 'emit'); - component.onSelect(new IdentityGroupModel({ name: 'group name'})); - fixture.whenStable().then(() => { - expect(selectEmitSpy).toHaveBeenCalled(); - }); - })); - - it('should show an error message if the group is invalid', async(() => { - fixture.detectChanges(); - checkGroupHasAccessSpy.and.returnValue(of(false)); - findGroupsByNameSpy.and.returnValue(of([])); - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'ZZZ'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { + it('should list the group if the typed result match', (done) => { + findGroupsByNameSpy.and.returnValue(of(mockIdentityGroups)); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'Mock'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); fixture.detectChanges(); - const errorMessage = element.querySelector('.adf-cloud-group-error-message'); - expect(errorMessage).not.toBeNull(); - expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUN'); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); + expect(findGroupsByNameSpy).toHaveBeenCalled(); + done(); + }); }); - })); - it('should show chip list when mode=multiple', async(() => { - component.mode = 'multiple'; - fixture.detectChanges(); - fixture.whenStable().then(() => { - const chip = element.querySelector('mat-chip-list'); - expect(chip).toBeDefined(); - }); - })); - - it('should not show chip list when mode=single', async(() => { - component.mode = 'single'; - fixture.detectChanges(); - fixture.whenStable().then(() => { - const chip = element.querySelector('mat-chip-list'); - expect(chip).toBeNull(); - }); - })); - - it('should pre-select all preSelectGroups when mode=multiple', async(() => { - component.mode = 'multiple'; - component.preSelectGroups = [{id: mockIdentityGroups[1].id}, {id: mockIdentityGroups[2].id}]; - fixture.detectChanges(); - fixture.whenStable().then(() => { + it('should hide result list if input is empty', (done) => { fixture.detectChanges(); - const chips = fixture.debugElement.queryAll(By.css('mat-chip')); - expect(chips.length).toBe(2); - }); - })); - - it('should not pre-select any group when preSelectGroups is empty and mode=multiple', async(() => { - component.mode = 'multiple'; - fixture.detectChanges(); - fixture.whenStable().then(() => { + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = ''; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); fixture.detectChanges(); - const chip = fixture.debugElement.query(By.css('mat-chip')); - expect(chip).toBeNull(); + fixture.whenStable().then(() => { + expect(element.querySelector('mat-option')).toBeNull(); + done(); + }); }); - })); - it('should pre-select preSelectGroups[0] when mode=single', async(() => { - component.mode = 'single'; - component.preSelectGroups = [{id: mockIdentityGroups[1].id}, {id: mockIdentityGroups[2].id}]; - fixture.detectChanges(); - fixture.whenStable().then(() => { - const selectedGroup = component.searchGroupsControl.value; - expect(selectedGroup.id).toBe(mockIdentityGroups[1].id); - }); - })); - - it('should not pre-select any group when preSelectGroups is empty and mode=single', async(() => { - component.mode = 'single'; - fixture.detectChanges(); - fixture.whenStable().then(() => { - const selectedGroup = component.searchGroupsControl.value; - expect(selectedGroup).toBeNull(); - }); - })); - - it('should emit removeGroup when a selected group is removed if mode=multiple', async(() => { - const removeGroupSpy = spyOn(component.removeGroup, 'emit'); - - component.mode = 'multiple'; - component.preSelectGroups = [{id: mockIdentityGroups[1].id}, {id: mockIdentityGroups[2].id}]; - fixture.detectChanges(); - - fixture.whenStable().then(() => { + it('should emit selectedGroup if option is valid', (done) => { fixture.detectChanges(); - const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon')); - removeIcon.nativeElement.click(); - - expect(removeGroupSpy).toHaveBeenCalledWith({ id: mockIdentityGroups[1].id }); - }); - - })); - - it('should list groups who have access to the app when appName is specified', async(() => { - component.appName = 'sample-app'; - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'M'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { + const selectEmitSpy = spyOn(component.selectGroup, 'emit'); + component.onSelect({ name: 'groupname' }); fixture.detectChanges(); - const groupsList = fixture.debugElement.queryAll(By.css('mat-option')); - expect(groupsList.length).toBe(mockIdentityGroups.length); + fixture.whenStable().then(() => { + expect(selectEmitSpy).toHaveBeenCalled(); + done(); + }); }); - })); - it('should not list groups who do not have access to the app when appName is specified', async(() => { - checkGroupHasAccessSpy.and.returnValue(of(false)); - component.appName = 'sample-app'; - - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('[data-automation-id="adf-cloud-group-search-input"]'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'Mock'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { + it('should show an error message if the search result empty', (done) => { + findGroupsByNameSpy.and.returnValue(of([])); fixture.detectChanges(); - const groupsList = fixture.debugElement.queryAll(By.css('mat-option')); - expect(groupsList.length).toBe(0); - }); - })); - - it('should filter groups when roles are specified', async(() => { - checkGroupHasGivenRoleSpy.and.returnValue(of(true)); - component.roles = ['mock-role-1', 'mock-role-2']; - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'M'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'ZZZ'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); fixture.detectChanges(); - const groupsList = fixture.debugElement.queryAll(By.css('mat-option')); - expect(groupsList.length).toBe(mockIdentityGroups.length); - expect(checkGroupHasGivenRoleSpy).toHaveBeenCalled(); + fixture.whenStable().then(() => { + inputHTMLElement.blur(); + fixture.detectChanges(); + const errorMessage = element.querySelector('.adf-cloud-group-error-message'); + expect(errorMessage).not.toBeNull(); + expect(errorMessage.textContent).toContain(' ADF_CLOUD_GROUPS.ERROR.NOT_FOUND '); + done(); + }); }); - })); - it('should return groups when roles are not specified', async(() => { - checkGroupHasGivenRoleSpy.and.returnValue(of(false)); - component.roles = []; - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'M'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { + it('should populate placeholder when title is present', (done) => { + component.title = 'TITLE_KEY'; fixture.detectChanges(); - const groupsList = fixture.debugElement.queryAll(By.css('mat-option')); - expect(groupsList.length).toBe(mockIdentityGroups.length); - expect(checkGroupHasGivenRoleSpy).not.toHaveBeenCalled(); + const matLabel: HTMLInputElement = element.querySelector('mat-label'); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(matLabel.textContent).toEqual('TITLE_KEY'); + done(); + }); }); - })); + }); - it('should validate access to the app when appName is specified', async(() => { - findGroupsByNameSpy.and.returnValue(of(mockIdentityGroups)); - checkGroupHasAccessSpy.and.returnValue(of(true)); - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'Mock'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(checkGroupHasAccessSpy).toHaveBeenCalledTimes(mockIdentityGroups.length); - }); - })); + describe('when application name defined', () => { - it('should not validate access to the app when appName is not specified', async(() => { - fixture.detectChanges(); - const inputHTMLElement: HTMLInputElement = element.querySelector('input'); - inputHTMLElement.focus(); - inputHTMLElement.value = 'M'; - inputHTMLElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(checkGroupHasAccessSpy).not.toHaveBeenCalled(); - }); - })); + let checkGroupHasAnyClientAppRoleSpy: jasmine.Spy; + let checkGroupHasClientAppSpy: jasmine.Spy; - it('should load the clients if appName change', async( () => { - component.appName = 'ADF'; - fixture.detectChanges(); - fixture.whenStable().then( () => { + beforeEach(async(() => { + findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); + checkGroupHasAnyClientAppRoleSpy = spyOn(identityGroupService, 'checkGroupHasAnyClientAppRole').and.returnValue(of(true)); + checkGroupHasClientAppSpy = spyOn(identityGroupService, 'checkGroupHasClientApp').and.returnValue(of(true)); + component.preSelectGroups = []; + component.appName = 'mock-app-name'; fixture.detectChanges(); - expect(getClientIdByApplicationNameSpy).toHaveBeenCalled(); - }); - })); + element = fixture.nativeElement; + })); - it('should filter users if appName change', async(() => { - component.appName = 'ADF'; - fixture.detectChanges(); - fixture.whenStable().then( () => { + it('should fetch the client ID if appName specified', async(() => { + const getClientIdByApplicationNameSpy = spyOn(identityGroupService, 'getClientIdByApplicationName').and.callThrough(); + component.appName = 'mock-app-name'; + const change = new SimpleChange(null, 'mock-app-name', false); + component.ngOnChanges({ 'appName': change }); fixture.detectChanges(); - expect(checkGroupHasAccessSpy).toHaveBeenCalled(); + fixture.whenStable().then( () => { + fixture.detectChanges(); + expect(getClientIdByApplicationNameSpy).toHaveBeenCalled(); + }); + })); + + it('should list groups who have access to the app when appName is specified', (done) => { + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); + done(); + }); }); - })); + + it('should not list groups who do not have access to the app when appName is specified', (done) => { + checkGroupHasClientAppSpy.and.returnValue(of(false)); + fixture.detectChanges(); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelectorAll('mat-option').length).toEqual(0); + done(); + }); + }); + + it('should list groups if given roles mapped with client roles', (done) => { + component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1']; + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); + expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled(); + done(); + }); + }); + + it('should not list groups if roles are not mapping with client roles', (done) => { + checkGroupHasAnyClientAppRoleSpy.and.returnValue(of(false)); + component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1']; + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(0); + expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled(); + done(); + }); + }); + + it('should not call client role mapping sevice if roles not specified', (done) => { + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(checkGroupHasAnyClientAppRoleSpy).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should validate access to the app when appName is specified', (done) => { + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(checkGroupHasClientAppSpy).toHaveBeenCalledTimes(5); + done(); + }); + }); + + it('should not validate access to the app when appName is not specified', (done) => { + component.appName = ''; + fixture.detectChanges(); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(checkGroupHasClientAppSpy).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should show an error message if the group does not have access', (done) => { + checkGroupHasClientAppSpy.and.returnValue(of(false)); + findGroupsByNameSpy.and.returnValue(of([])); + fixture.detectChanges(); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'ZZZ'; + inputHTMLElement.dispatchEvent(new Event('keyup')); + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + inputHTMLElement.blur(); + fixture.detectChanges(); + const errorMessage = element.querySelector('.adf-cloud-group-error-message'); + expect(errorMessage).not.toBeNull(); + expect(errorMessage.textContent).toContain(' ADF_CLOUD_GROUPS.ERROR.NOT_FOUND '); + done(); + }); + }); + }); + + describe('Single Mode and Pre-selected groups', () => { + + beforeEach(async(() => { + component.mode = 'single'; + component.preSelectGroups = mockIdentityGroups; + fixture.detectChanges(); + element = fixture.nativeElement; + })); + + it('should not show chip list when mode=single', (done) => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + const chip = element.querySelector('mat-chip-list'); + expect(chip).toBeNull(); + done(); + }); + }); + + it('should not pre-select any group when preSelectGroups is empty and mode=single', (done) => { + component.preSelectGroups = []; + fixture.detectChanges(); + fixture.whenStable().then(() => { + const selectedUser = component.searchGroupsControl.value; + expect(selectedUser).toBeNull(); + done(); + }); + }); + }); + + describe('Multiple Mode and Pre-selected groups', () => { + + const change = new SimpleChange(null, mockIdentityGroups, false); + + beforeEach(async(() => { + component.mode = 'multiple'; + component.preSelectGroups = mockIdentityGroups; + component.ngOnChanges({ 'preSelectGroups': change }); + fixture.detectChanges(); + element = fixture.nativeElement; + })); + + it('should show chip list when mode=multiple', (done) => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + const chip = element.querySelector('mat-chip-list'); + expect(chip).toBeDefined(); + done(); + }); + }); + + it('should pre-select all preSelectGroups when mode=multiple', (done) => { + component.mode = 'multiple'; + fixture.detectChanges(); + component.ngOnChanges({ 'preSelectUsers': change }); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + const chips = fixture.debugElement.queryAll(By.css('mat-chip')); + expect(chips.length).toBe(5); + done(); + }); + }); + + it('should emit removeGroup when a selected group is removed', (done) => { + const removeSpy = spyOn(component.removeGroup, 'emit'); + component.mode = 'multiple'; + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon')); + removeIcon.nativeElement.click(); + fixture.detectChanges(); + expect(removeSpy).toHaveBeenCalled(); + done(); + }); + }); + }); + + describe('When roles defined', () => { + + let checkGroupHasRoleSpy: jasmine.Spy; + + beforeEach(async(() => { + component.roles = ['mock-role-1', 'mock-role-2']; + spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups)); + checkGroupHasRoleSpy = spyOn(identityGroupService, 'checkGroupHasRole').and.returnValue(of(true)); + fixture.detectChanges(); + element = fixture.nativeElement; + })); + + it('should filter if groups has any specified role', (done) => { + fixture.detectChanges(); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); + expect(checkGroupHasRoleSpy).toHaveBeenCalledTimes(5); + done(); + }); + }); + + it('should not filter groups if user does not have any specified role', (done) => { + fixture.detectChanges(); + checkGroupHasRoleSpy.and.returnValue(of(false)); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelectorAll('mat-option').length).toEqual(0); + expect(checkGroupHasRoleSpy).toHaveBeenCalled(); + done(); + }); + }); + + it('should not call checkGroupHasRole service when roles are not specified', (done) => { + component.roles = []; + fixture.detectChanges(); + const inputHTMLElement: HTMLInputElement = element.querySelector('input'); + inputHTMLElement.focus(); + inputHTMLElement.value = 'M'; + inputHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(checkGroupHasRoleSpy).not.toHaveBeenCalled(); + done(); + }); + }); + }); + + describe('Multiple Mode with read-only', () => { + + it('Should not be able to remove pre-selected groups if readonly property set to true', (done) => { + fixture.detectChanges(); + const preselectedGroups = [ + { id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: true }, + { id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: true } + ]; + component.preSelectGroups = preselectedGroups; + const change = new SimpleChange(null, preselectedGroups, false); + component.mode = 'multiple'; + const removeGroupSpy = spyOn(component.removeGroup, 'emit'); + component.ngOnChanges({ 'preSelectGroups': change }); + fixture.detectChanges(); + const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip'); + const removeIcon = fixture.nativeElement.querySelector('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]'); + expect(chipList.length).toBe(2); + expect(component.preSelectGroups[0].readonly).toBeTruthy(); + expect(component.preSelectGroups[1].readonly).toBeTruthy(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + removeIcon.click(); + fixture.detectChanges(); + expect(removeGroupSpy).not.toHaveBeenCalled(); + expect(component.preSelectGroups.length).toBe(2); + expect(component.preSelectGroups[0].readonly).toBe(true, 'Not removable'); + expect(component.preSelectGroups[1].readonly).toBe(true, 'Not removable'); + expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(2); + done(); + }); + }); + + it('Should be able to remove preselected groups if readonly property set to false', (done) => { + fixture.detectChanges(); + const preselectedGroups = [ + { id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: false }, + { id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: false } + ]; + component.preSelectGroups = preselectedGroups; + const change = new SimpleChange(null, preselectedGroups, false); + component.mode = 'multiple'; + const removeGroupSpy = spyOn(component.removeGroup, 'emit'); + component.ngOnChanges({ 'preSelectGroups': change }); + fixture.detectChanges(); + const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip'); + const removeIcon = fixture.nativeElement.querySelector('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]'); + expect(chipList.length).toBe(2); + expect(component.preSelectGroups[0].readonly).toBe(false, 'Removable'); + expect(component.preSelectGroups[1].readonly).toBe(false, 'Removable'); + removeIcon.click(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + removeIcon.click(); + fixture.detectChanges(); + expect(removeGroupSpy).toHaveBeenCalled(); + expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1); + done(); + }); + }); + }); }); diff --git a/lib/process-services-cloud/src/lib/group/pipe/group-initial.pipe.spec.ts b/lib/process-services-cloud/src/lib/group/pipe/group-initial.pipe.spec.ts index d6b1073b34..139710ab21 100644 --- a/lib/process-services-cloud/src/lib/group/pipe/group-initial.pipe.spec.ts +++ b/lib/process-services-cloud/src/lib/group/pipe/group-initial.pipe.spec.ts @@ -25,7 +25,7 @@ describe('InitialGroupNamePipe', () => { beforeEach(() => { pipe = new InitialGroupNamePipe(); - fakeGroup = new IdentityGroupModel({name: 'mock'}); + fakeGroup = {name: 'mock'}; }); it('should return with the group initial', () => {