mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-3812] Add multi selection and roles filtering to adf-cloud-people component (#4068)
* [ADF-3812] Added multiple user selection and user pre-selection * [ADF-3812] Added tests * [ADF-3812] Added jsdoc * [ADF-3812] Improved variable naming * [ADF-3812] Improved mode selection * [ADF-3812] Changed input name and emit logic * [ADF-3812] Used modified emitter name in start task * [ADF-3812] Improved default role selection * Use the new strategy to fetch the authorized users * * Fixed pre-selection in single mode * * Added invalid selection validation * * Added start task assignee validation * * Improved preset loading * * Improved tests * * Added test to validate default assignee * * Added methods to check user has access to an app * * Added app access to people cloud and start task * * Refactored methods and removed unused input - showCurrentUser * * Added tests * * Changed service names and removed unwated services * * Used formControl error instead of manual error flag * * Used new hasError method of people component inside start task * * Improved tests * * Updated callCustomApi call signature * * Added documentation * * Disabled search until clientId is retrieved * * Changed realm name * * Added jsdoc for service methods * Remove the useless doc
This commit is contained in:
committed by
Eugenio Romano
parent
46150a65f2
commit
f08ad08d0f
45
docs/process-services-cloud/people-cloud.component.md
Normal file
45
docs/process-services-cloud/people-cloud.component.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
Title: People Cloud Component
|
||||
Added: v3.0.0
|
||||
Status: Active
|
||||
Last reviewed: 2019-09-01
|
||||
---
|
||||
|
||||
# [People Cloud Component](../../lib/process-services-cloud/src/lib/process-services-cloud/src/lib/task/start-task/components/people-cloud/people-cloud.component.ts")
|
||||
|
||||
An autosuggest input control that allows single or multiple users to be selected based on the input parameters.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Class members](#class-members)
|
||||
- [Properties](#properties)
|
||||
- [Events](#events)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<adf-cloud-people
|
||||
[appName]="'simple-app'"
|
||||
[mode]="'multiple'">
|
||||
</adf-cloud-people>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| appName | `string` | | Name of the application. If specified, shows the users who have access to the app. |
|
||||
| mode | `string` | 'single' | Mode of the user selection (single/multiple). |
|
||||
| roles | `string[]` | | Role names of the users to be listed. |
|
||||
| preSelectUsers | `IdentityUserModel[]` | | Array of users to be pre-selected. Pre-select all users in `multiple` mode and only the first user of the array in `single` mode. |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| selectUser | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`IdentityUserModel`](../../lib/core/userinfo/models/identity-user.model.ts)`>` | Emitted when a user is selected. |
|
||||
| removeUser | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`IdentityUserModel`](../../lib/core/userinfo/models/identity-user.model.ts)`>` | Emitted when a selected user is removed in `multiple` mode. |
|
||||
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<Any>` | Emitted when an error occurs. |
|
@@ -17,12 +17,13 @@
|
||||
|
||||
import { ApiService } from '../APS-cloud/apiservice';
|
||||
import { Util } from '../../util/util';
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
|
||||
export class Identity {
|
||||
|
||||
api: ApiService = new ApiService();
|
||||
|
||||
constructor() {
|
||||
constructor(appConfig: AppConfigService) {
|
||||
}
|
||||
|
||||
async init(username, password) {
|
||||
|
@@ -199,4 +199,52 @@ describe('IdentityUserService', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return true when user has access to an application', (done) => {
|
||||
spyOn(service, 'getClientIdByApplicationName').and.returnValue(of('mock-client'));
|
||||
spyOn(service, 'getClientRoles').and.returnValue(of(mockRoles));
|
||||
|
||||
service.checkUserHasClientApp('user-id', 'app-name').subscribe(
|
||||
(res: boolean) => {
|
||||
expect(res).toBeTruthy();
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false when user does not have access to an application', (done) => {
|
||||
spyOn(service, 'getClientIdByApplicationName').and.returnValue(of('mock-client'));
|
||||
spyOn(service, 'getClientRoles').and.returnValue(of([]));
|
||||
|
||||
service.checkUserHasClientApp('user-id', 'app-name').subscribe(
|
||||
(res: boolean) => {
|
||||
expect(res).toBeFalsy();
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return true when user has any given application role', (done) => {
|
||||
spyOn(service, 'getClientIdByApplicationName').and.returnValue(of('mock-client'));
|
||||
spyOn(service, 'getClientRoles').and.returnValue(of(mockRoles));
|
||||
|
||||
service.checkUserHasAnyClientAppRole('user-id', 'app-name', [mockRoles[1].name] ).subscribe(
|
||||
(res: boolean) => {
|
||||
expect(res).toBeTruthy();
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false when user does not have any given application role', (done) => {
|
||||
spyOn(service, 'getClientIdByApplicationName').and.returnValue(of('mock-client'));
|
||||
spyOn(service, 'getClientRoles').and.returnValue(of([]));
|
||||
|
||||
service.checkUserHasAnyClientAppRole('user-id', 'app-name', [mockRoles[1].name]).subscribe(
|
||||
(res: boolean) => {
|
||||
expect(res).toBeFalsy();
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable, from, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { IdentityUserModel } from '../models/identity-user.model';
|
||||
import { JwtHelperService } from '../../services/jwt-helper.service';
|
||||
@@ -44,7 +44,6 @@ export class IdentityUserService {
|
||||
|
||||
/**
|
||||
* Gets the name and other basic details of the current user.
|
||||
* @returns User details
|
||||
*/
|
||||
getCurrentUserInfo(): IdentityUserModel {
|
||||
const familyName = this.getValueFromToken<string>(IdentityUserService.FAMILY_NAME);
|
||||
@@ -57,8 +56,6 @@ export class IdentityUserService {
|
||||
|
||||
/**
|
||||
* Gets a named value from the user access token.
|
||||
* @param key Key name of the field to retrieve
|
||||
* @returns Value associated with the key
|
||||
*/
|
||||
getValueFromToken<T>(key: string): T {
|
||||
let value;
|
||||
@@ -70,9 +67,123 @@ export class IdentityUserService {
|
||||
return <T> value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find users based on search input.
|
||||
*/
|
||||
findUsersByName(search: string): Observable<any> {
|
||||
if (search === '') {
|
||||
return of([]);
|
||||
}
|
||||
const url = this.buildUserUrl();
|
||||
const httpMethod = 'GET', pathParams = {}, queryParams = {search: search}, bodyParam = {}, headerParams = {},
|
||||
formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
|
||||
|
||||
return (from(this.apiService.getInstance().oauth2Auth.callCustomApi(
|
||||
url, httpMethod, pathParams, queryParams,
|
||||
headerParams, formParams, bodyParam,
|
||||
contentTypes, accepts, Object, null, null)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client roles of a user for a particular client.
|
||||
*/
|
||||
getClientRoles(userId: string, clientId: string): Observable<any[]> {
|
||||
const url = this.buildUserClientRoleMapping(userId, clientId);
|
||||
const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {},
|
||||
formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
|
||||
|
||||
return from(this.apiService.getInstance().oauth2Auth.callCustomApi(
|
||||
url, httpMethod, pathParams, queryParams,
|
||||
headerParams, formParams, bodyParam,
|
||||
contentTypes, accepts, Object, null, null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether user has access to a client app.
|
||||
*/
|
||||
checkUserHasClientApp(userId: string, clientId: string): Observable<boolean> {
|
||||
return this.getClientRoles(userId, clientId).pipe(
|
||||
map((clientRoles: any[]) => {
|
||||
if (clientRoles.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether user has any of client app role.
|
||||
*/
|
||||
checkUserHasAnyClientAppRole(userId: string, clientId: string, roleNames: string[]): Observable<boolean> {
|
||||
return this.getClientRoles(userId, clientId).pipe(
|
||||
map((clientRoles: any[]) => {
|
||||
let hasRole = false;
|
||||
if (clientRoles.length > 0) {
|
||||
roleNames.forEach((roleName) => {
|
||||
const role = clientRoles.find((availableRole) => {
|
||||
return availableRole.name === roleName;
|
||||
});
|
||||
|
||||
if (role) {
|
||||
hasRole = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
return hasRole;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client id for an application.
|
||||
*/
|
||||
getClientIdByApplicationName(applicationName: string): Observable<string> {
|
||||
const url = this.buildGetClientsUrl();
|
||||
const httpMethod = 'GET', pathParams = {}, queryParams = {clientId: applicationName}, bodyParam = {}, headerParams = {}, formParams = {},
|
||||
contentTypes = ['application/json'], accepts = ['application/json'];
|
||||
return from(this.apiService.getInstance()
|
||||
.oauth2Auth.callCustomApi(url, httpMethod, pathParams, queryParams, headerParams,
|
||||
formParams, bodyParam, contentTypes,
|
||||
accepts, Object, null, null)
|
||||
).pipe(
|
||||
map((response: any[]) => {
|
||||
const clientId = response && response.length > 0 ? response[0].id : '';
|
||||
return clientId;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a user has access to an application
|
||||
* @param userId Id of the user
|
||||
* @param applicationName Name of the application
|
||||
* @returns Boolean
|
||||
*/
|
||||
checkUserHasApplicationAccess(userId: string, applicationName: string): Observable<boolean> {
|
||||
return this.getClientIdByApplicationName(applicationName).pipe(
|
||||
switchMap((clientId: string) => {
|
||||
return this.checkUserHasClientApp(userId, clientId);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a user has any application role
|
||||
*/
|
||||
checkUserHasAnyApplicationRole(userId: string, applicationName: string, roleNames: string[]): Observable<boolean> {
|
||||
return this.getClientIdByApplicationName(applicationName).pipe(
|
||||
switchMap((clientId: string) => {
|
||||
return this.checkUserHasAnyClientAppRole(userId, clientId, roleNames);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets details for all users.
|
||||
* @returns Array of user info objects
|
||||
*/
|
||||
getUsers(): Observable<IdentityUserModel[]> {
|
||||
const url = this.buildUserUrl();
|
||||
@@ -92,8 +203,6 @@ export class IdentityUserService {
|
||||
|
||||
/**
|
||||
* Gets a list of roles for a user.
|
||||
* @param userId ID of the user
|
||||
* @returns Array of role info objects
|
||||
*/
|
||||
getUserRoles(userId: string): Observable<IdentityRoleModel[]> {
|
||||
const url = this.buildRolesUrl(userId);
|
||||
@@ -113,8 +222,6 @@ export class IdentityUserService {
|
||||
|
||||
/**
|
||||
* Gets an array of users (including the current user) who have any of the roles in the supplied list.
|
||||
* @param roleNames List of role names to look for
|
||||
* @returns Array of user info objects
|
||||
*/
|
||||
async getUsersByRolesWithCurrentUser(roleNames: string[]): Promise<IdentityUserModel[]> {
|
||||
const filteredUsers: IdentityUserModel[] = [];
|
||||
@@ -134,8 +241,6 @@ export class IdentityUserService {
|
||||
|
||||
/**
|
||||
* Gets an array of users (not including the current user) who have any of the roles in the supplied list.
|
||||
* @param roleNames List of role names to look for
|
||||
* @returns Array of user info objects
|
||||
*/
|
||||
async getUsersByRolesWithoutCurrentUser(roleNames: string[]): Promise<IdentityUserModel[]> {
|
||||
const filteredUsers: IdentityUserModel[] = [];
|
||||
@@ -173,8 +278,16 @@ export class IdentityUserService {
|
||||
return `${this.appConfigService.get('identityHost')}/users`;
|
||||
}
|
||||
|
||||
private buildUserClientRoleMapping(userId: string, clientId: string): any {
|
||||
return `${this.appConfigService.get('identityHost')}/users/${userId}/role-mappings/clients/${clientId}`;
|
||||
}
|
||||
|
||||
private buildRolesUrl(userId: string): any {
|
||||
return `${this.appConfigService.get('identityHost')}/users/${userId}/role-mappings/realm/composite`;
|
||||
}
|
||||
|
||||
private buildGetClientsUrl() {
|
||||
return `${this.appConfigService.get('identityHost')}/clients`;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,18 +1,40 @@
|
||||
<form>
|
||||
<mat-form-field class="adf-people-cloud">
|
||||
<mat-label id="assignee-id">{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE' | translate}}</mat-label>
|
||||
<input #inputValue
|
||||
<mat-label id="assignee-id">{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE' | translate}}</mat-label>
|
||||
<mat-chip-list #userChipList *ngIf="isMultipleMode(); else singleSelection">
|
||||
<mat-chip
|
||||
*ngFor="let user of selectedUsers$ | async"
|
||||
(removed)="onRemove(user)">
|
||||
{{user | fullName}}
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
<input
|
||||
#userInput
|
||||
matInput
|
||||
[formControl]="searchUserCtrl"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputFor]="userChipList"
|
||||
class="adf-cloud-input"
|
||||
(focus)="setFocus(true)"
|
||||
(blur)="setFocus(false)"
|
||||
data-automation-id="adf-people-cloud-search-input">
|
||||
</mat-chip-list>
|
||||
|
||||
<ng-template #singleSelection>
|
||||
<input matInput
|
||||
(focus)="setFocus(true)"
|
||||
(blur)="setFocus(false)"
|
||||
class="adf-cloud-input"
|
||||
data-automation-id="adf-people-cloud-search-input"
|
||||
type="text"
|
||||
[formControl]="searchUser"
|
||||
[formControl]="searchUserCtrl"
|
||||
[matAutocomplete]="auto">
|
||||
</ng-template>
|
||||
<mat-autocomplete autoActiveFirstOption class="adf-people-cloud-list"
|
||||
#auto="matAutocomplete"
|
||||
(optionSelected)="onSelect($event.option.value)"
|
||||
[displayWith]="getDisplayName">
|
||||
<mat-option *ngFor="let user of users$ | async; let i = index" [value]="user">
|
||||
<mat-option *ngFor="let user of searchUsers$ | async; let i = index" [value]="user">
|
||||
<div class="adf-people-cloud-row" id="adf-people-cloud-user-{{i}}">
|
||||
<div [outerHTML]="user | usernameInitials:'adf-people-widget-pic'"></div>
|
||||
<span class="adf-people-label-name"> {{user | fullName}}</span>
|
||||
@@ -21,7 +43,7 @@
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<div class="adf-start-task-cloud-error">
|
||||
<div *ngIf="dataError" fxLayout="row" fxLayoutAlign="start start" [@transitionMessages]="_subscriptAnimationState">
|
||||
<div *ngIf="hasErrorMessage()" fxLayout="row" fxLayoutAlign="start start" [@transitionMessages]="_subscriptAnimationState">
|
||||
<div class="adf-start-task-cloud-error-message">{{ 'ADF_CLOUD_START_TASK.ERROR.MESSAGE' | translate }}</div>
|
||||
<mat-icon class="adf-start-task-cloud-error-icon">warning</mat-icon>
|
||||
</div>
|
||||
|
@@ -20,7 +20,7 @@ import { By } from '@angular/platform-browser';
|
||||
import { PeopleCloudComponent } from './people-cloud.component';
|
||||
import { StartTaskCloudTestingModule } from '../../testing/start-task-cloud.testing.module';
|
||||
import { LogService, setupTestBed, IdentityUserService, IdentityUserModel } from '@alfresco/adf-core';
|
||||
import { mockUsers, mockRoles } from '../../mock/user-cloud.mock';
|
||||
import { mockUsers } from '../../mock/user-cloud.mock';
|
||||
import { of } from 'rxjs';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||
|
||||
@@ -29,8 +29,8 @@ describe('PeopleCloudComponent', () => {
|
||||
let fixture: ComponentFixture<PeopleCloudComponent>;
|
||||
let element: HTMLElement;
|
||||
let identityService: IdentityUserService;
|
||||
let getRolesByUserIdSpy: jasmine.Spy;
|
||||
let getUserSpy: jasmine.Spy;
|
||||
let findUsersSpy: jasmine.Spy;
|
||||
let checkUserHasAccessSpy: jasmine.Spy;
|
||||
|
||||
setupTestBed({
|
||||
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
|
||||
@@ -42,40 +42,17 @@ describe('PeopleCloudComponent', () => {
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
identityService = TestBed.get(IdentityUserService);
|
||||
getRolesByUserIdSpy = spyOn(identityService, 'getUserRoles').and.returnValue(of(mockRoles));
|
||||
getUserSpy = spyOn(identityService, 'getUsers').and.returnValue(of(mockUsers));
|
||||
findUsersSpy = spyOn(identityService, 'findUsersByName').and.returnValue(of(mockUsers));
|
||||
checkUserHasAccessSpy = spyOn(identityService, 'checkUserHasClientApp').and.returnValue(of(true));
|
||||
spyOn(identityService, 'getClientIdByApplicationName').and.returnValue(of('mock-client-id'));
|
||||
});
|
||||
|
||||
it('should create PeopleCloudComponent', () => {
|
||||
expect(component instanceof PeopleCloudComponent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should able to fetch users', () => {
|
||||
fixture.detectChanges();
|
||||
expect(getUserSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should able to fetch roles by user id', async(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(getRolesByUserIdSpy).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not list the current logged in user when showCurrentUser is false', async(() => {
|
||||
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(mockUsers[1]);
|
||||
component.showCurrentUser = false;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const currentUser = component.users.find((user) => {
|
||||
return user.username === mockUsers[1].username;
|
||||
});
|
||||
expect(currentUser).toBeUndefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show the users if the typed result match', async(() => {
|
||||
component.users$ = of(<IdentityUserModel[]> mockUsers);
|
||||
component.searchUsers$ = of(<IdentityUserModel[]> mockUsers);
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
@@ -90,7 +67,7 @@ describe('PeopleCloudComponent', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should hide result list if input is empty', () => {
|
||||
it('should hide result list if input is empty', async(() => {
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
@@ -99,40 +76,176 @@ describe('PeopleCloudComponent', () => {
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('mat-option'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('#adf-people-cloud-user-0'))).toBeNull();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit selectedUser if option is valid', async() => {
|
||||
it('should emit selectedUser if option is valid', async(() => {
|
||||
fixture.detectChanges();
|
||||
let selectEmitSpy = spyOn(component.selectedUser, 'emit');
|
||||
let selectEmitSpy = spyOn(component.selectUser, 'emit');
|
||||
component.onSelect(new IdentityUserModel({ username: 'username'}));
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(selectEmitSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show an error message if the user is invalid', async(() => {
|
||||
getUserSpy.and.returnValue(of([]));
|
||||
getRolesByUserIdSpy.and.returnValue(of([]));
|
||||
component.dataError = true;
|
||||
checkUserHasAccessSpy.and.returnValue(of(false));
|
||||
findUsersSpy.and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
inputHTMLElement.dispatchEvent(new Event('keyup'));
|
||||
inputHTMLElement.dispatchEvent(new Event('keydown'));
|
||||
inputHTMLElement.value = 'ZZZ';
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
inputHTMLElement.blur();
|
||||
fixture.detectChanges();
|
||||
const errorMessage = element.querySelector('.adf-start-task-cloud-error-message');
|
||||
expect(element.querySelector('.adf-start-task-cloud-error')).not.toBeNull();
|
||||
expect(errorMessage).not.toBeNull();
|
||||
expect(errorMessage.textContent).toContain('ADF_CLOUD_START_TASK.ERROR.MESSAGE');
|
||||
});
|
||||
}));
|
||||
|
||||
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 preSelectUsers when mode=multiple', async(() => {
|
||||
spyOn(identityService, 'getUsersByRolesWithCurrentUser').and.returnValue(Promise.resolve(mockUsers));
|
||||
component.mode = 'multiple';
|
||||
component.preSelectUsers = <any> [{id: mockUsers[1].id}, {id: mockUsers[2].id}];
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
expect(chips.length).toBe(2);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not pre-select any user when preSelectUsers is empty and mode=multiple', async(() => {
|
||||
spyOn(identityService, 'getUsersByRolesWithCurrentUser').and.returnValue(Promise.resolve(mockUsers));
|
||||
component.mode = 'multiple';
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const chip = fixture.debugElement.query(By.css('mat-chip'));
|
||||
expect(chip).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should pre-select preSelectUsers[0] when mode=single', async(() => {
|
||||
spyOn(identityService, 'getUsersByRolesWithCurrentUser').and.returnValue(Promise.resolve(mockUsers));
|
||||
component.mode = 'single';
|
||||
component.preSelectUsers = <any> [{id: mockUsers[1].id}, {id: mockUsers[2].id}];
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const selectedUser = component.searchUserCtrl.value;
|
||||
expect(selectedUser.id).toBe(mockUsers[1].id);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not pre-select any user when preSelectUsers is empty and mode=single', async(() => {
|
||||
spyOn(identityService, 'getUsersByRolesWithCurrentUser').and.returnValue(Promise.resolve(mockUsers));
|
||||
component.mode = 'single';
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const selectedUser = component.searchUserCtrl.value;
|
||||
expect(selectedUser).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit removeUser when a selected user is removed if mode=multiple', async(() => {
|
||||
spyOn(identityService, 'getUsersByRolesWithCurrentUser').and.returnValue(Promise.resolve(mockUsers));
|
||||
let removeUserSpy = spyOn(component.removeUser, 'emit');
|
||||
|
||||
component.mode = 'multiple';
|
||||
component.preSelectUsers = <any> [{id: mockUsers[1].id}, {id: mockUsers[2].id}];
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon'));
|
||||
removeIcon.nativeElement.click();
|
||||
|
||||
expect(removeUserSpy).toHaveBeenCalledWith({ id: mockUsers[1].id });
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should list users who have access to the app when appName is specified', async(() => {
|
||||
component.appName = 'sample-app';
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
inputHTMLElement.value = 'M';
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const usersList = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(usersList.length).toBe(mockUsers.length);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not list users who do not have access to the app when appName is specified', async(() => {
|
||||
checkUserHasAccessSpy.and.returnValue(of(false));
|
||||
component.appName = 'sample-app';
|
||||
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
inputHTMLElement.value = 'M';
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const usersList = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(usersList.length).toBe(0);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should validate access to the app when appName is specified', async(() => {
|
||||
component.appName = 'sample-app';
|
||||
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
inputHTMLElement.value = 'M';
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(checkUserHasAccessSpy).toHaveBeenCalledTimes(mockUsers.length);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not validate access to the app when appName is not specified', async(() => {
|
||||
fixture.detectChanges();
|
||||
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
|
||||
inputHTMLElement.focus();
|
||||
inputHTMLElement.value = 'M';
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(checkUserHasAccessSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
@@ -16,8 +16,9 @@
|
||||
*/
|
||||
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Component, OnInit, Output, EventEmitter, ViewEncapsulation, Input } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { Component, OnInit, Output, EventEmitter, ViewEncapsulation, Input, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Observable, of, BehaviorSubject } from 'rxjs';
|
||||
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter } from 'rxjs/operators';
|
||||
import { FullNamePipe, IdentityUserModel, IdentityUserService } from '@alfresco/adf-core';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
|
||||
@@ -40,71 +41,227 @@ import { trigger, state, style, transition, animate } from '@angular/animations'
|
||||
|
||||
export class PeopleCloudComponent implements OnInit {
|
||||
|
||||
static ROLE_ACTIVITI_ADMIN = 'ACTIVITI_ADMIN';
|
||||
static ROLE_ACTIVITI_USER = 'ACTIVITI_USER';
|
||||
static ROLE_ACTIVITI_MODELER = 'ACTIVITI_MODELER';
|
||||
static MODE_SINGLE = 'single';
|
||||
static MODE_MULTIPLE = 'multiple';
|
||||
|
||||
/** Show current user in the list or not. */
|
||||
/** Name of the application. If specified, shows the users who have access to the app. */
|
||||
@Input()
|
||||
showCurrentUser: boolean = true;
|
||||
appName: string;
|
||||
|
||||
/** Mode of the user selection (single/multiple). */
|
||||
@Input()
|
||||
mode: string = PeopleCloudComponent.MODE_SINGLE;
|
||||
|
||||
/** Role names of the users to be listed. */
|
||||
@Input()
|
||||
roles: string[];
|
||||
|
||||
/** Array of users to be pre-selected. Pre-select all users in multi selection mode and only the first user of the array in single selection mode. */
|
||||
@Input()
|
||||
preSelectUsers: IdentityUserModel[];
|
||||
|
||||
/** Emitted when a user is selected. */
|
||||
@Output()
|
||||
selectedUser: EventEmitter<IdentityUserModel> = new EventEmitter<IdentityUserModel>();
|
||||
selectUser: EventEmitter<IdentityUserModel> = new EventEmitter<IdentityUserModel>();
|
||||
|
||||
/** Emitted when a selected user is removed in multi selection mode. */
|
||||
@Output()
|
||||
removeUser: EventEmitter<IdentityUserModel> = new EventEmitter<IdentityUserModel>();
|
||||
|
||||
/** Emitted when an error occurs. */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
users$: Observable<IdentityUserModel[]>;
|
||||
@ViewChild('userInput')
|
||||
private userInput: ElementRef<HTMLInputElement>;
|
||||
|
||||
searchUser: FormControl = new FormControl();
|
||||
private _selectedUsers: IdentityUserModel[] = [];
|
||||
private _searchUsers: IdentityUserModel[] = [];
|
||||
private selectedUsers: BehaviorSubject<IdentityUserModel[]>;
|
||||
private searchUsers: BehaviorSubject<IdentityUserModel[]>;
|
||||
selectedUsers$: Observable<IdentityUserModel[]>;
|
||||
searchUsers$: Observable<IdentityUserModel[]>;
|
||||
|
||||
searchUserCtrl: FormControl = new FormControl();
|
||||
|
||||
_subscriptAnimationState: string = 'enter';
|
||||
|
||||
users: IdentityUserModel[] = [];
|
||||
clientId: string;
|
||||
|
||||
dataError = false;
|
||||
isFocused: boolean;
|
||||
|
||||
currentUser: IdentityUserModel;
|
||||
|
||||
constructor(private identityUserService: IdentityUserService) { }
|
||||
constructor(private identityUserService: IdentityUserService) {
|
||||
this.selectedUsers = new BehaviorSubject<IdentityUserModel[]>(this._selectedUsers);
|
||||
this.searchUsers = new BehaviorSubject<IdentityUserModel[]>(this._searchUsers);
|
||||
this.selectedUsers$ = this.selectedUsers.asObservable();
|
||||
this.searchUsers$ = this.searchUsers.asObservable();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadUsers();
|
||||
if (this.hasPreSelectUsers()) {
|
||||
this.loadPreSelectUsers();
|
||||
}
|
||||
|
||||
this.initSearch();
|
||||
|
||||
if (this.appName) {
|
||||
this.disableSearch();
|
||||
this.loadClientId();
|
||||
}
|
||||
}
|
||||
|
||||
initSearch() {
|
||||
this.searchUser.valueChanges.subscribe((keyword) => {
|
||||
this.users$ = this.searchUsers(keyword);
|
||||
});
|
||||
}
|
||||
|
||||
private async loadUsers() {
|
||||
const roles = [PeopleCloudComponent.ROLE_ACTIVITI_ADMIN, PeopleCloudComponent.ROLE_ACTIVITI_MODELER, PeopleCloudComponent.ROLE_ACTIVITI_USER];
|
||||
if (this.showCurrentUser) {
|
||||
this.users = await this.identityUserService.getUsersByRolesWithCurrentUser(roles);
|
||||
private initSearch() {
|
||||
this.searchUserCtrl.valueChanges.pipe(
|
||||
filter((value) => {
|
||||
return typeof value === 'string';
|
||||
}),
|
||||
tap((value) => {
|
||||
if (value) {
|
||||
this.setError();
|
||||
} else {
|
||||
this.users = await this.identityUserService.getUsersByRolesWithoutCurrentUser(roles);
|
||||
this.clearError();
|
||||
}
|
||||
}),
|
||||
debounceTime(500),
|
||||
distinctUntilChanged(),
|
||||
tap(() => {
|
||||
this.resetSearchUsers();
|
||||
}),
|
||||
switchMap((search) => this.identityUserService.findUsersByName(search)),
|
||||
mergeMap((users) => {
|
||||
return users;
|
||||
}),
|
||||
filter((user: any) => {
|
||||
return !this.isUserAlreadySelected(user);
|
||||
}),
|
||||
mergeMap((user: any) => {
|
||||
if (this.appName) {
|
||||
return this.checkUserHasAccess(user.id).pipe(
|
||||
mergeMap((hasRole) => {
|
||||
return hasRole ? of(user) : of();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return of(user);
|
||||
}
|
||||
|
||||
private searchUsers(keyword: string): Observable<IdentityUserModel[]> {
|
||||
const filteredUsers = this.users.filter((user) => {
|
||||
return user.username.toLowerCase().indexOf(keyword.toString().toLowerCase()) !== -1;
|
||||
})
|
||||
).subscribe((user) => {
|
||||
this._searchUsers.push(user);
|
||||
this.searchUsers.next(this._searchUsers);
|
||||
});
|
||||
this.dataError = filteredUsers.length === 0;
|
||||
return of(filteredUsers);
|
||||
}
|
||||
|
||||
onSelect(selectedUser: IdentityUserModel) {
|
||||
this.selectedUser.emit(selectedUser);
|
||||
this.dataError = false;
|
||||
private checkUserHasAccess(userId: string): Observable<boolean> {
|
||||
if (this.hasRoles()) {
|
||||
return this.identityUserService.checkUserHasAnyClientAppRole(userId, this.clientId, this.roles);
|
||||
} else {
|
||||
return this.identityUserService.checkUserHasClientApp(userId, this.clientId);
|
||||
}
|
||||
}
|
||||
|
||||
private hasRoles(): boolean {
|
||||
return this.roles && this.roles.length > 0;
|
||||
}
|
||||
|
||||
private isUserAlreadySelected(user: IdentityUserModel): boolean {
|
||||
if (this._selectedUsers && this._selectedUsers.length > 0) {
|
||||
const result = this._selectedUsers.find((selectedUser) => {
|
||||
return selectedUser.id === user.id;
|
||||
});
|
||||
|
||||
return !!result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private loadPreSelectUsers() {
|
||||
if (this.isMultipleMode()) {
|
||||
if (this.preSelectUsers && this.preSelectUsers.length > 0) {
|
||||
this.selectedUsers.next(this.preSelectUsers);
|
||||
}
|
||||
} else {
|
||||
this.selectedUsers.next(this.preSelectUsers);
|
||||
this.searchUserCtrl.setValue(this.preSelectUsers[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadClientId() {
|
||||
this.clientId = await this.identityUserService.getClientIdByApplicationName(this.appName).toPromise();
|
||||
|
||||
if (this.clientId) {
|
||||
this.enableSearch();
|
||||
}
|
||||
}
|
||||
|
||||
onSelect(user: IdentityUserModel) {
|
||||
if (this.isMultipleMode()) {
|
||||
|
||||
if (!this.isUserAlreadySelected(user)) {
|
||||
this._selectedUsers.push(user);
|
||||
this.selectedUsers.next(this._selectedUsers);
|
||||
this.selectUser.emit(user);
|
||||
}
|
||||
|
||||
this.userInput.nativeElement.value = '';
|
||||
this.searchUserCtrl.setValue('');
|
||||
} else {
|
||||
this.selectUser.emit(user);
|
||||
}
|
||||
|
||||
this.clearError();
|
||||
this.resetSearchUsers();
|
||||
}
|
||||
|
||||
onRemove(user: IdentityUserModel) {
|
||||
this.removeUser.emit(user);
|
||||
const indexToRemove = this._selectedUsers.findIndex((selectedUser) => { return selectedUser.id === user.id; });
|
||||
this._selectedUsers.splice(indexToRemove, 1);
|
||||
this.selectedUsers.next(this._selectedUsers);
|
||||
}
|
||||
|
||||
getDisplayName(user): string {
|
||||
return FullNamePipe.prototype.transform(user);
|
||||
}
|
||||
|
||||
isMultipleMode(): boolean {
|
||||
return this.mode === PeopleCloudComponent.MODE_MULTIPLE;
|
||||
}
|
||||
|
||||
private hasPreSelectUsers(): boolean {
|
||||
return this.preSelectUsers && this.preSelectUsers.length > 0;
|
||||
}
|
||||
|
||||
private resetSearchUsers() {
|
||||
this._searchUsers = [];
|
||||
this.searchUsers.next(this._searchUsers);
|
||||
}
|
||||
|
||||
private setError() {
|
||||
this.searchUserCtrl.setErrors({invalid: true});
|
||||
}
|
||||
|
||||
private clearError() {
|
||||
this.searchUserCtrl.setErrors(null);
|
||||
}
|
||||
|
||||
setFocus(isFocused: boolean) {
|
||||
this.isFocused = isFocused;
|
||||
}
|
||||
|
||||
hasError(): boolean {
|
||||
return !!this.searchUserCtrl.errors;
|
||||
}
|
||||
|
||||
hasErrorMessage(): boolean {
|
||||
return !this.isFocused && this.hasError();
|
||||
}
|
||||
|
||||
private disableSearch() {
|
||||
this.searchUserCtrl.disable();
|
||||
}
|
||||
|
||||
private enableSearch() {
|
||||
this.searchUserCtrl.enable();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex>
|
||||
<adf-cloud-people (selectedUser)="onAssigneeSelect($event)"></adf-cloud-people>
|
||||
<adf-cloud-people #peopleInput *ngIf="currentUser" [appName]="appName" [preSelectUsers]="[currentUser]" (selectUser)="onAssigneeSelect($event)"></adf-cloud-people>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
@@ -78,7 +78,7 @@
|
||||
</button>
|
||||
<button
|
||||
color="primary"
|
||||
type="submit" [disabled]="dateError || !taskForm.valid || submitted"
|
||||
type="submit" [disabled]="dateError || !taskForm.valid || submitted || assignee.hasError()"
|
||||
mat-button
|
||||
id="button-start">
|
||||
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}}
|
||||
|
@@ -32,7 +32,6 @@ import { taskDetailsMock } from '../mock/task-details.mock';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ProcessServiceCloudTestingModule } from './../../../testing/process-service-cloud.testing.module';
|
||||
import { StartTaskCloudTestingModule } from '../testing/start-task-cloud.testing.module';
|
||||
import { mockRoles, mockUsers } from '../mock/user-cloud.mock';
|
||||
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
|
||||
|
||||
describe('StartTaskCloudComponent', () => {
|
||||
@@ -43,8 +42,6 @@ describe('StartTaskCloudComponent', () => {
|
||||
let identityService: IdentityUserService;
|
||||
let element: HTMLElement;
|
||||
let createNewTaskSpy: jasmine.Spy;
|
||||
let getRolesByUserIdSpy: jasmine.Spy;
|
||||
let getUserSpy: jasmine.Spy;
|
||||
|
||||
setupTestBed({
|
||||
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
|
||||
@@ -60,9 +57,7 @@ describe('StartTaskCloudComponent', () => {
|
||||
service = TestBed.get(StartTaskCloudService);
|
||||
identityService = TestBed.get(IdentityUserService);
|
||||
createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(of(taskDetailsMock));
|
||||
getRolesByUserIdSpy = spyOn(identityService, 'getUserRoles').and.returnValue(of(mockRoles));
|
||||
getUserSpy = spyOn(identityService, 'getUsers').and.returnValue(of(mockUsers));
|
||||
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(new IdentityUserModel({username: 'currentUser'}));
|
||||
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(new IdentityUserModel({username: 'currentUser', firstName: 'Test', lastName: 'User'}));
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
@@ -70,27 +65,8 @@ describe('StartTaskCloudComponent', () => {
|
||||
expect(component instanceof StartTaskCloudComponent).toBe(true, 'should create StartTaskCloudComponent');
|
||||
});
|
||||
|
||||
it('should defined adf-cloud-people and fetch users ', () => {
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const peopleElement = fixture.debugElement.nativeElement.querySelector('adf-cloud-people');
|
||||
expect(peopleElement).toBeDefined();
|
||||
expect(getRolesByUserIdSpy).toHaveBeenCalled();
|
||||
expect(getUserSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('create task', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
createNewTaskSpy.and.returnValue(of(
|
||||
{
|
||||
id: 91,
|
||||
name: 'fakeName',
|
||||
assignee: 'fake-assignee'
|
||||
}
|
||||
));
|
||||
});
|
||||
|
||||
it('should create new task when start button is clicked', async(() => {
|
||||
let successSpy = spyOn(component.success, 'emit');
|
||||
component.taskForm.controls['name'].setValue('fakeName');
|
||||
@@ -113,11 +89,7 @@ describe('StartTaskCloudComponent', () => {
|
||||
createTaskButton.click();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(successSpy).toHaveBeenCalledWith({
|
||||
id: 91,
|
||||
name: 'fakeName',
|
||||
assignee: 'fake-assignee'
|
||||
});
|
||||
expect(successSpy).toHaveBeenCalledWith(taskDetailsMock);
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -143,20 +115,18 @@ describe('StartTaskCloudComponent', () => {
|
||||
expect(successSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should assign task when an assignee is selected', async(() => {
|
||||
let successSpy = spyOn(component.success, 'emit');
|
||||
it('should assign task to the logged in user when invalid assignee is selected', async(() => {
|
||||
component.taskForm.controls['name'].setValue('fakeName');
|
||||
component.assigneeName = 'fake-assignee';
|
||||
fixture.detectChanges();
|
||||
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
|
||||
const assigneeInput = <HTMLElement> element.querySelector('input.adf-cloud-input');
|
||||
assigneeInput.nodeValue = 'a';
|
||||
fixture.detectChanges();
|
||||
const createTaskButton = <HTMLElement> element.querySelector('#button-start');
|
||||
createTaskButton.click();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(successSpy).toHaveBeenCalledWith({
|
||||
id: 91,
|
||||
name: 'fakeName',
|
||||
assignee: 'fake-assignee'
|
||||
});
|
||||
const taskRequest = new TaskDetailsCloudModel({ name: 'fakeName', assignee: 'currentUser'});
|
||||
expect(createNewTaskSpy).toHaveBeenCalledWith(taskRequest);
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -173,12 +143,18 @@ describe('StartTaskCloudComponent', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
it('should select logged in user as assignee by default', () => {
|
||||
fixture.detectChanges();
|
||||
const assignee = fixture.nativeElement.querySelector('[data-automation-id="adf-people-cloud-search-input"]');
|
||||
expect(assignee.value).toBe('Test User');
|
||||
});
|
||||
|
||||
it('should show start task button', () => {
|
||||
component.taskForm.controls['name'].setValue('fakeName');
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#button-start')).toBeDefined();
|
||||
expect(element.querySelector('#button-start')).not.toBeNull();
|
||||
expect(element.querySelector('#button-start').textContent).toContain('ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START');
|
||||
const startButton = element.querySelector('#button-start');
|
||||
expect(startButton).toBeDefined();
|
||||
expect(startButton.textContent).toContain('ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START');
|
||||
});
|
||||
|
||||
it('should disable start button if name is empty', () => {
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
|
||||
import { MOMENT_DATE_FORMATS, MomentDateAdapter } from '@alfresco/adf-core';
|
||||
import moment from 'moment-es6';
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
IdentityUserService,
|
||||
IdentityUserModel
|
||||
} from '@alfresco/adf-core';
|
||||
import { PeopleCloudComponent } from './people-cloud/people-cloud.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-start-task',
|
||||
@@ -70,6 +71,9 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@ViewChild('peopleInput')
|
||||
assignee: PeopleCloudComponent;
|
||||
|
||||
users$: Observable<any[]>;
|
||||
|
||||
taskId: string;
|
||||
|
Reference in New Issue
Block a user