AAE-20141 Replace adf mat selectors process services (#9514)

* AAE-20141 removing mat from unit tests in process-services

* AAE-20141 fix dropdown false-positives

* AAE-20141 dropdown editor remake

* AAE-20141 remove remaining selectors

* AAE-20141 fix radio buttons selector
This commit is contained in:
Wojciech Duda
2024-04-09 14:26:37 +02:00
committed by GitHub
parent b081800274
commit 3bfadae286
15 changed files with 536 additions and 680 deletions

View File

@@ -24,6 +24,9 @@ import { ProcessTestingModule } from '../testing/process.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { mockEmittedTaskAttachments, mockTaskAttachments } from '../mock/task/task-attachments.mock';
import { ProcessContentService } from '../form/services/process-content.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatMenuItemHarness } from '@angular/material/menu/testing';
describe('TaskAttachmentList', () => {
@@ -35,6 +38,7 @@ describe('TaskAttachmentList', () => {
let getFileRawContentSpy: jasmine.Spy;
let getContentPreviewSpy: jasmine.Spy;
let disposableSuccess: any;
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -46,6 +50,7 @@ describe('TaskAttachmentList', () => {
});
fixture = TestBed.createComponent(TaskAttachmentListComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
service = TestBed.inject(ProcessContentService);
@@ -146,11 +151,12 @@ describe('TaskAttachmentList', () => {
fixture.detectChanges();
await fixture.whenStable();
const actionMenu = window.document.querySelectorAll('button.mat-menu-item').length;
const actionMenuItems = await loader.getAllHarnesses(MatMenuItemHarness);
expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull();
expect(actionMenu).toBe(3);
expect(actionMenuItems.length).toBe(3);
});
it('should not display remove action if attachments are read only', async () => {
@@ -166,11 +172,11 @@ describe('TaskAttachmentList', () => {
fixture.detectChanges();
await fixture.whenStable();
const actionMenu = window.document.querySelectorAll('button.mat-menu-item').length;
const actionMenuItems = await loader.getAllHarnesses(MatMenuItemHarness);
expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).toBeNull();
expect(actionMenu).toBe(2);
expect(actionMenuItems.length).toBe(2);
});
it('should show the empty list component when the attachments list is empty', async () => {

View File

@@ -35,17 +35,16 @@ import { TranslateModule } from '@ngx-translate/core';
import { TaskService } from './services/task.service';
import { TaskFormService } from './services/task-form.service';
import { TaskRepresentation } from '@alfresco/js-api';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
describe('FormComponent UI and visibility', () => {
let component: FormComponent;
let taskService: TaskService;
let taskFormService: TaskFormService;
let fixture: ComponentFixture<FormComponent>;
const openSelect = () => {
const dropdown = fixture.debugElement.nativeElement.querySelector('.mat-select-trigger');
dropdown.click();
};
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -54,6 +53,7 @@ describe('FormComponent UI and visibility', () => {
});
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
taskService = TestBed.inject(TaskService);
taskFormService = TestBed.inject(TaskFormService);
});
@@ -124,30 +124,23 @@ describe('FormComponent UI and visibility', () => {
const change = new SimpleChange(null, 1, true);
component.ngOnChanges({ taskId: change });
fixture.detectChanges();
await fixture.whenStable();
openSelect();
fixture.detectChanges();
await fixture.whenStable();
const options = fixture.debugElement.queryAll(By.css('.mat-option-text'));
const dropdown = await loader.getHarness(MatSelectHarness);
await dropdown.open();
const options = await dropdown.getOptions();
const optOne = options[1];
const optTwo = options[2];
const optThree = options[3];
expect(optOne.nativeElement.innerText.trim()).toEqual('united kingdom');
expect(optTwo.nativeElement.innerText.trim()).toEqual('italy');
expect(optThree.nativeElement.innerText.trim()).toEqual('france');
expect((await optOne.getText()).trim()).toEqual('united kingdom');
expect((await optTwo.getText()).trim()).toEqual('italy');
expect((await optThree.getText()).trim()).toEqual('france');
optTwo.nativeElement.click();
await optTwo.click();
fixture.detectChanges();
await fixture.whenStable();
const dropdown = fixture.debugElement.queryAll(By.css('#country'));
expect(dropdown[0].nativeElement.innerText.trim()).toEqual('italy');
const dropdownCountries = fixture.debugElement.queryAll(By.css('#country'));
expect(dropdownCountries[0].nativeElement.innerText.trim()).toEqual('italy');
});
describe('Visibility conditions', () => {

View File

@@ -29,6 +29,11 @@ import { WidgetVisibilityService, FormModel, FormOutcomeModel } from '@alfresco/
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { ProcessTestingModule } from '../testing/process.testing.module';
import { ProcessService } from '../process-list/services/process.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
import { MatCardHarness } from '@angular/material/card/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
describe('StartFormComponent', () => {
@@ -38,6 +43,7 @@ describe('StartFormComponent', () => {
let visibilityService: WidgetVisibilityService;
let translate: TranslateService;
let processService: ProcessService;
let loader: HarnessLoader;
const exampleId1 = 'my:process1';
const exampleId2 = 'my:process2';
@@ -52,6 +58,7 @@ describe('StartFormComponent', () => {
});
fixture = TestBed.createComponent(StartFormComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
processService = TestBed.inject(ProcessService);
visibilityService = TestBed.inject(WidgetVisibilityService);
translate = TestBed.inject(TranslateService);
@@ -243,8 +250,8 @@ describe('StartFormComponent', () => {
const dropdownField = formFields.find((field) => field.id === 'mockTypeDropDown');
const dropdownWidget = fixture.debugElement.nativeElement.querySelector('dropdown-widget');
const dropdownLabel = fixture.debugElement.nativeElement.querySelector('.adf-dropdown-widget .adf-label');
const selectElement = fixture.debugElement.nativeElement.querySelector('.adf-select .mat-select-trigger');
selectElement.click();
const selectElement = await loader.getHarness(MatSelectHarness);
await selectElement.open();
expect(selectElement).toBeTruthy();
expect(dropdownWidget).toBeTruthy();
@@ -300,7 +307,7 @@ describe('StartFormComponent', () => {
const formFieldsWidget = fixture.debugElement.nativeElement.querySelector('form-field');
const inputElement = fixture.debugElement.nativeElement.querySelector('.adf-input');
const inputLabelElement = fixture.debugElement.nativeElement.querySelector('.mat-form-field-infix > .adf-label');
const inputLabelElement = fixture.debugElement.nativeElement.querySelector('.adf-label');
const dateElement = fixture.debugElement.nativeElement.querySelector('#billdate');
const dateLabelElement = fixture.debugElement.nativeElement.querySelector('#billdate-label');
const selectElement = fixture.debugElement.nativeElement.querySelector('#claimtype');
@@ -322,14 +329,9 @@ describe('StartFormComponent', () => {
component.showOutcomeButtons = true;
component.showRefreshButton = true;
component.ngOnChanges({ processDefinitionId: new SimpleChange(exampleId1, exampleId2, true) });
fixture.detectChanges();
await fixture.whenStable();
const refreshElement = fixture.debugElement.nativeElement.querySelector('.mat-card-actions>button');
refreshElement.click();
fixture.detectChanges();
await fixture.whenStable();
const refreshElement = await (await loader.getHarness(MatCardHarness)).getHarness(MatButtonHarness);
await refreshElement.click();
/* cspell:disable-next-line */
const selectElement = fixture.debugElement.nativeElement.querySelector('#claimtype');
@@ -370,17 +372,17 @@ describe('StartFormComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const titleElement = fixture.debugElement.nativeElement.querySelector('mat-card-title>h2');
const actionButtons = fixture.debugElement.nativeElement.querySelectorAll('.mat-button');
const cardTitle = await loader.getHarness(MatCardHarness);
const actionButtons = await loader.getAllHarnesses(MatButtonHarness);
expect(titleElement.innerText.trim()).toEqual('Mock Title');
expect(await cardTitle.getTitleText()).toEqual('Mock Title');
expect(actionButtons.length).toBe(4);
expect(actionButtons[0].innerText.trim()).toBe('SAVE');
expect(actionButtons[0].disabled).toBeFalsy();
expect(actionButtons[1].innerText.trim()).toBe('APPROVE');
expect(actionButtons[1].disabled).toBeTruthy();
expect(actionButtons[2].innerText.trim()).toBe('COMPLETE');
expect(actionButtons[2].disabled).toBeTruthy();
expect(await actionButtons[0].getText()).toBe('SAVE');
expect(await actionButtons[0].isDisabled()).toBeFalsy();
expect(await actionButtons[1].getText()).toBe('APPROVE');
expect(await actionButtons[1].isDisabled()).toBeTruthy();
expect(await actionButtons[2].getText()).toBe('COMPLETE');
expect(await actionButtons[2].isDisabled()).toBeTruthy();
});
});

View File

@@ -16,46 +16,34 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Observable, of } from 'rxjs';
import {
WidgetVisibilityService,
FormFieldOption,
FormFieldModel,
FormModel,
FormFieldTypes,
CoreTestingModule
} from '@alfresco/adf-core';
import { WidgetVisibilityService, FormFieldOption, FormFieldModel, FormModel, FormFieldTypes, CoreTestingModule } from '@alfresco/adf-core';
import { DropdownWidgetComponent } from './dropdown.widget';
import { TranslateModule } from '@ngx-translate/core';
import { TaskFormService } from '../../services/task-form.service';
import { ProcessDefinitionService } from '../../services/process-definition.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
describe('DropdownWidgetComponent', () => {
let taskFormService: TaskFormService;
let processDefinitionService: ProcessDefinitionService;
let widget: DropdownWidgetComponent;
let visibilityService: WidgetVisibilityService;
let fixture: ComponentFixture<DropdownWidgetComponent>;
let element: HTMLElement;
const openSelect = () => {
const dropdown = fixture.debugElement.nativeElement.querySelector('.mat-select-trigger');
dropdown.click();
};
let loader: HarnessLoader;
const fakeOptionList: FormFieldOption[] = [
{id: 'opt_1', name: 'option_1'},
{id: 'opt_2', name: 'option_2'},
{id: 'opt_3', name: 'option_3'}];
{ id: 'opt_1', name: 'option_1' },
{ id: 'opt_2', name: 'option_2' },
{ id: 'opt_3', name: 'option_3' }
];
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule
]
imports: [TranslateModule.forRoot(), CoreTestingModule]
});
fixture = TestBed.createComponent(DropdownWidgetComponent);
widget = fixture.componentInstance;
@@ -64,6 +52,7 @@ describe('DropdownWidgetComponent', () => {
visibilityService = TestBed.inject(WidgetVisibilityService);
processDefinitionService = TestBed.inject(ProcessDefinitionService);
widget.field = new FormFieldModel(new FormModel());
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should require field with restUrl', () => {
@@ -73,7 +62,7 @@ describe('DropdownWidgetComponent', () => {
widget.ngOnInit();
expect(taskFormService.getRestFieldValues).not.toHaveBeenCalled();
widget.field = new FormFieldModel(null, {restUrl: null});
widget.field = new FormFieldModel(null, { restUrl: null });
widget.ngOnInit();
expect(taskFormService.getRestFieldValues).not.toHaveBeenCalled();
});
@@ -102,14 +91,17 @@ describe('DropdownWidgetComponent', () => {
});
it('should preserve empty option when loading fields', () => {
const restFieldValue: FormFieldOption = {id: '1', name: 'Option1'} as FormFieldOption;
spyOn(taskFormService, 'getRestFieldValues').and.callFake(() => new Observable((observer) => {
observer.next([restFieldValue]);
observer.complete();
}));
const restFieldValue: FormFieldOption = { id: '1', name: 'Option1' } as FormFieldOption;
spyOn(taskFormService, 'getRestFieldValues').and.callFake(
() =>
new Observable((observer) => {
observer.next([restFieldValue]);
observer.complete();
})
);
const form = new FormModel({taskId: '<id>'});
const emptyOption: FormFieldOption = {id: 'empty', name: 'Empty'} as FormFieldOption;
const form = new FormModel({ taskId: '<id>' });
const emptyOption: FormFieldOption = { id: 'empty', name: 'Empty' } as FormFieldOption;
widget.field = new FormFieldModel(form, {
id: '<id>',
restUrl: '/some/url/address',
@@ -125,9 +117,8 @@ describe('DropdownWidgetComponent', () => {
});
describe('when is required', () => {
beforeEach(() => {
widget.field = new FormFieldModel(new FormModel({taskId: '<id>'}), {
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
type: FormFieldTypes.DROPDOWN,
required: true
});
@@ -167,37 +158,31 @@ describe('DropdownWidgetComponent', () => {
});
describe('when template is ready', () => {
describe('and dropdown is populated via taskId', () => {
beforeEach(() => {
spyOn(visibilityService, 'refreshVisibility').and.stub();
spyOn(taskFormService, 'getRestFieldValues').and.callFake(() => of(fakeOptionList));
widget.field = new FormFieldModel(new FormModel({taskId: 'fake-task-id'}), {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
readOnly: 'false',
restUrl: 'fake-rest-url'
});
widget.field.emptyOption = {id: 'empty', name: 'Choose one...'};
widget.field.emptyOption = { id: 'empty', name: 'Choose one...' };
widget.field.isVisible = true;
fixture.detectChanges();
});
it('should show visible dropdown widget', async () => {
expect(element.querySelector('#dropdown-id')).toBeDefined();
expect(element.querySelector('#dropdown-id')).not.toBeNull();
const dropdown = await loader.getHarness(MatSelectHarness.with({ selector: '#dropdown-id' }));
await dropdown.open();
const options = await dropdown.getOptions();
openSelect();
const optOne = fixture.debugElement.queryAll(By.css('[id="mat-option-1"]'));
const optTwo = fixture.debugElement.queryAll(By.css('[id="mat-option-2"]'));
const optThree = fixture.debugElement.queryAll(By.css('[id="mat-option-3"]'));
expect(optOne).not.toBeNull();
expect(optTwo).not.toBeNull();
expect(optThree).not.toBeNull();
expect(await options[0].getText()).toBe(widget.field.emptyOption.name);
expect(await options[1].getText()).toBe(fakeOptionList[0].name);
expect(await options[2].getText()).toBe(fakeOptionList[1].name);
expect(await options[3].getText()).toBe(fakeOptionList[2].name);
});
it('should select the default value when an option is chosen as default', async () => {
@@ -216,13 +201,7 @@ describe('DropdownWidgetComponent', () => {
widget.field.value = 'empty';
widget.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
openSelect();
fixture.detectChanges();
await fixture.whenStable();
await (await loader.getHarness(MatSelectHarness)).open();
const dropDownElement: any = element.querySelector('#dropdown-id');
expect(dropDownElement.attributes['ng-reflect-model'].value).toBe('empty');
@@ -230,35 +209,30 @@ describe('DropdownWidgetComponent', () => {
});
describe('and dropdown is populated via processDefinitionId', () => {
beforeEach(() => {
spyOn(visibilityService, 'refreshVisibility').and.stub();
spyOn(processDefinitionService, 'getRestFieldValuesByProcessId').and.callFake(() => of(fakeOptionList));
widget.field = new FormFieldModel(new FormModel({processDefinitionId: 'fake-process-id'}), {
widget.field = new FormFieldModel(new FormModel({ processDefinitionId: 'fake-process-id' }), {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
readOnly: 'false',
restUrl: 'fake-rest-url'
});
widget.field.emptyOption = {id: 'empty', name: 'Choose one...'};
widget.field.emptyOption = { id: 'empty', name: 'Choose one...' };
widget.field.isVisible = true;
fixture.detectChanges();
});
it('should show visible dropdown widget', () => {
expect(element.querySelector('#dropdown-id')).toBeDefined();
expect(element.querySelector('#dropdown-id')).not.toBeNull();
it('should show visible dropdown widget', async () => {
const dropdown = await loader.getHarness(MatSelectHarness.with({ selector: '#dropdown-id' }));
await dropdown.open();
const options = await dropdown.getOptions();
openSelect();
const optOne = fixture.debugElement.queryAll(By.css('[id="mat-option-1"]'));
const optTwo = fixture.debugElement.queryAll(By.css('[id="mat-option-2"]'));
const optThree = fixture.debugElement.queryAll(By.css('[id="mat-option-3"]'));
expect(optOne).not.toBeNull();
expect(optTwo).not.toBeNull();
expect(optThree).not.toBeNull();
expect(await options[0].getText()).toBe(widget.field.emptyOption.name);
expect(await options[1].getText()).toBe(fakeOptionList[0].name);
expect(await options[2].getText()).toBe(fakeOptionList[1].name);
expect(await options[3].getText()).toBe(fakeOptionList[2].name);
});
it('should select the default value when an option is chosen as default', async () => {
@@ -276,21 +250,14 @@ describe('DropdownWidgetComponent', () => {
it('should select the empty value when no default is chosen', async () => {
widget.field.value = 'empty';
widget.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
openSelect();
fixture.detectChanges();
await fixture.whenStable();
await (await loader.getHarness(MatSelectHarness)).open();
const dropDownElement: any = element.querySelector('#dropdown-id');
expect(dropDownElement.attributes['ng-reflect-model'].value).toBe('empty');
});
it('should be disabled when the field is readonly', async () => {
widget.field = new FormFieldModel(new FormModel({processDefinitionId: 'fake-process-id'}), {
widget.field = new FormFieldModel(new FormModel({ processDefinitionId: 'fake-process-id' }), {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
@@ -307,25 +274,18 @@ describe('DropdownWidgetComponent', () => {
});
it('should show the option value when the field is readonly', async () => {
widget.field = new FormFieldModel(new FormModel({processDefinitionId: 'fake-process-id'}), {
widget.field = new FormFieldModel(new FormModel({ processDefinitionId: 'fake-process-id' }), {
id: 'dropdown-id',
name: 'date-name',
type: 'readonly',
value: 'FakeValue',
readOnly: true,
params: {field: {name: 'date-name', type: 'dropdown'}}
params: { field: { name: 'date-name', type: 'dropdown' } }
});
openSelect();
const select = await loader.getHarness(MatSelectHarness);
fixture.detectChanges();
await fixture.whenStable();
const options = fixture.debugElement.queryAll(By.css('.mat-option-text'));
expect(options.length).toBe(1);
const option = options[0].nativeElement;
expect(option.innerText).toEqual('FakeValue');
expect(await select.getValueText()).toEqual('FakeValue');
});
});
});

View File

@@ -13,4 +13,5 @@
<mat-option *ngFor="let opt of options" [value]="opt.name" [id]="opt.id">{{opt.name}}</mat-option>
</mat-select>
</mat-form-field>
<span *ngFor="let opt of options" id="testme">{{opt | json}}</span>
</div>

View File

@@ -16,31 +16,33 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Observable, throwError } from 'rxjs';
import {
AlfrescoApiService,
CoreTestingModule,
FormFieldModel,
FormModel,
FormService
} from '@alfresco/adf-core';
import { Observable, of, throwError } from 'rxjs';
import { FormFieldModel, FormModel, FormService, TranslationMock, TranslationService } from '@alfresco/adf-core';
import { DynamicTableColumnOption } from '../models/dynamic-table-column-option.model';
import { DynamicTableColumn } from '../models/dynamic-table-column.model';
import { DynamicTableRow } from '../models/dynamic-table-row.model';
import { DynamicTableModel } from '../models/dynamic-table.widget.model';
import { DropdownEditorComponent } from './dropdown.editor';
import { TranslateModule } from '@ngx-translate/core';
import { TaskFormService } from '../../../../services/task-form.service';
import { ProcessDefinitionService } from '../../../../services/process-definition.service';
import { MatSelectHarness } from '@angular/material/select/testing';
import { FormModule } from '../../../../form.module';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatOptionModule } from '@angular/material/core';
describe('DropdownEditorComponent', () => {
let fixture: ComponentFixture<DropdownEditorComponent>;
let component: DropdownEditorComponent;
let loader: HarnessLoader;
let formService: FormService;
let taskFormService: TaskFormService;
let processDefinitionService: ProcessDefinitionService;
let alfrescoApiService: AlfrescoApiService;
let form: FormModel;
let table: DynamicTableModel;
let column: DynamicTableColumn;
@@ -50,254 +52,211 @@ describe('DropdownEditorComponent', () => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule
]
CommonModule,
NoopAnimationsModule,
MatFormFieldModule,
MatSelectModule,
MatOptionModule,
FormModule
],
providers: [{ provide: TranslationService, useClass: TranslationMock }]
});
alfrescoApiService = TestBed.inject(AlfrescoApiService);
formService = TestBed.inject(FormService);
taskFormService = TestBed.inject(TaskFormService);
processDefinitionService = TestBed.inject(ProcessDefinitionService);
formService = new FormService();
taskFormService = new TaskFormService(alfrescoApiService, null);
processDefinitionService = new ProcessDefinitionService(alfrescoApiService, null);
fixture = TestBed.createComponent(DropdownEditorComponent);
component = fixture.componentInstance;
row = {value: {dropdown: 'one'}} as DynamicTableRow;
row = { value: { dropdown: 'one' } } as DynamicTableRow;
column = {
id: 'dropdown',
options: [
{id: '1', name: 'one'},
{id: '2', name: 'two'}
]
{ id: '1', name: 'one' },
{ id: '2', name: 'two' }
],
editable: true
} as DynamicTableColumn;
form = new FormModel({taskId: '<task-id>'});
table = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'}), formService);
form = new FormModel({ taskId: '<task-id>' });
table = new DynamicTableModel(new FormFieldModel(form, { id: '<field-id>', isVisible: true }), formService);
table.rows.push(row);
table.columns.push(column);
component = new DropdownEditorComponent(formService, taskFormService, processDefinitionService, null);
component.table = table;
component.row = row;
component.column = column;
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should require table field to setup', () => {
table.field = null;
component.ngOnInit();
expect(component.value).toBeNull();
expect(component.options).toEqual([]);
});
describe('dropdown is populated manually', () => {
beforeEach(() => {
column = {
id: 'dropdown',
options: [
{ id: '1', name: 'one' },
{ id: '2', name: 'two' }
]
} as DynamicTableColumn;
it('should setup with manual mode', () => {
row.value[column.id] = 'two';
component.ngOnInit();
expect(component.options).toEqual(column.options);
expect(component.value).toBe(row.value[column.id]);
});
component.column = column;
});
it('should setup empty columns for manual mode', () => {
column.options = null;
component.ngOnInit();
expect(component.options).toEqual([]);
});
it('should setup with REST mode', () => {
column.optionType = 'rest';
row.value[column.id] = 'twelve';
const restResults: DynamicTableColumnOption[] = [
{id: '11', name: 'eleven'},
{id: '12', name: 'twelve'}
];
spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(
new Observable((observer) => {
observer.next(restResults);
observer.complete();
})
);
component.ngOnInit();
expect(taskFormService.getRestFieldValuesColumn).toHaveBeenCalledWith(
form.taskId,
table.field.id,
column.id
);
expect(column.options).toEqual(restResults);
expect(component.options).toEqual(restResults);
expect(component.value).toBe(row.value[column.id]);
});
it('should create empty options array on REST response', () => {
column.optionType = 'rest';
spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(
new Observable((observer) => {
observer.next(null);
observer.complete();
})
);
component.ngOnInit();
expect(taskFormService.getRestFieldValuesColumn).toHaveBeenCalledWith(
form.taskId,
table.field.id,
column.id
);
expect(column.options).toEqual([]);
expect(component.options).toEqual([]);
expect(component.value).toBe(row.value[column.id]);
});
it('should handle REST error getting options with task id', () => {
column.optionType = 'rest';
const error = 'error';
spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(
throwError(error)
);
spyOn(component, 'handleError').and.stub();
component.ngOnInit();
expect(component.handleError).toHaveBeenCalledWith(error);
});
it('should handle REST error getting option with processDefinitionId', () => {
column.optionType = 'rest';
const procForm = new FormModel({processDefinitionId: '<process-definition-id>'});
const procTable = new DynamicTableModel(new FormFieldModel(procForm, {id: '<field-id>'}), formService);
component.table = procTable;
const error = 'error';
spyOn(processDefinitionService, 'getRestFieldValuesColumnByProcessId').and.returnValue(
throwError(error)
);
spyOn(component, 'handleError').and.stub();
component.ngOnInit();
expect(component.handleError).toHaveBeenCalledWith(error);
});
it('should update row on value change', () => {
const event = {value: 'two'};
component.onValueChanged(row, column, event);
expect(row.value[column.id]).toBe(column.options[1]);
});
describe('when template is ready', () => {
let dropDownEditorComponent: DropdownEditorComponent;
let fixture: ComponentFixture<DropdownEditorComponent>;
let element: HTMLElement;
let dynamicTable: DynamicTableModel;
const openSelect = () => {
const dropdown = fixture.debugElement.query(By.css('.mat-select-trigger'));
dropdown.triggerEventHandler('click', null);
it('should require table field to setup', () => {
table.field = null;
fixture.detectChanges();
};
expect(component.value).toBeNull();
expect(component.options).toEqual([]);
});
it('should setup with manual mode', () => {
row.value[column.id] = 'two';
fixture.detectChanges();
expect(component.options).toEqual(column.options);
expect(component.value).toBe(row.value[column.id]);
});
it('should setup empty columns for manual mode', () => {
column.options = null;
fixture.detectChanges();
expect(component.options).toEqual([]);
});
it('should setup with REST mode', () => {
column.optionType = 'rest';
row.value[column.id] = 'twelve';
const restResults: DynamicTableColumnOption[] = [
{ id: '11', name: 'eleven' },
{ id: '12', name: 'twelve' }
];
spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(
new Observable((observer) => {
observer.next(restResults);
observer.complete();
})
);
fixture.detectChanges();
expect(taskFormService.getRestFieldValuesColumn).toHaveBeenCalledWith(form.taskId, table.field.id, column.id);
expect(column.options).toEqual(restResults);
expect(component.options).toEqual(restResults);
expect(component.value).toBe(row.value[column.id]);
});
it('should create empty options array on REST response', () => {
column.optionType = 'rest';
spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(
new Observable((observer) => {
observer.next(null);
observer.complete();
})
);
fixture.detectChanges();
expect(taskFormService.getRestFieldValuesColumn).toHaveBeenCalledWith(form.taskId, table.field.id, column.id);
expect(column.options).toEqual([]);
expect(component.options).toEqual([]);
expect(component.value).toBe(row.value[column.id]);
});
it('should handle REST error getting options with task id', () => {
column.optionType = 'rest';
const error = 'error';
spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(throwError(error));
spyOn(component, 'handleError').and.stub();
component.ngOnInit();
expect(component.handleError).toHaveBeenCalledWith(error);
});
it('should handle REST error getting option with processDefinitionId', () => {
column.optionType = 'rest';
const procForm = new FormModel({ processDefinitionId: '<process-definition-id>' });
const procTable = new DynamicTableModel(new FormFieldModel(procForm, { id: '<field-id>' }), formService);
component.table = procTable;
const error = 'error';
spyOn(processDefinitionService, 'getRestFieldValuesColumnByProcessId').and.returnValue(throwError(error));
spyOn(component, 'handleError').and.stub();
fixture.detectChanges();
expect(component.handleError).toHaveBeenCalledWith(error);
});
it('should update row on value change', () => {
const event = { value: 'two' };
component.onValueChanged(row, column, event);
expect(row.value[column.id]).toBe(column.options[1]);
});
});
describe('dropdown is populated via taskId', () => {
let getRestFieldValuesColumnSpy: jasmine.Spy;
beforeEach(async () => {
form = new FormModel({ taskId: '<task-id>' });
table = new DynamicTableModel(new FormFieldModel(form, { id: '<field-id>' }), formService);
component.table = table;
component.table.field = new FormFieldModel(form, {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
readOnly: 'false',
restUrl: 'fake-rest-url'
});
component.column.optionType = 'rest';
component.table.field.isVisible = true;
getRestFieldValuesColumnSpy = spyOn(taskFormService, 'getRestFieldValuesColumn').and.returnValue(of(column.options));
});
it('should show visible dropdown widget', async () => {
const select = await loader.getHarness(MatSelectHarness.with({ selector: '#dropdown' }));
await select.open();
const options = await select.getOptions();
expect(getRestFieldValuesColumnSpy).toHaveBeenCalled();
expect(component.options.length).toBe(2);
expect(options.length).toBe(3);
});
});
describe('dropdown is populated via processDefinitionId', () => {
let getRestFieldValuesColumnByProcessId: jasmine.Spy;
beforeEach(() => {
fixture = TestBed.createComponent(DropdownEditorComponent);
dropDownEditorComponent = fixture.componentInstance;
element = fixture.nativeElement;
form = new FormModel({ processDefinitionId: '<proc-id>' });
table = new DynamicTableModel(new FormFieldModel(form, { id: '<field-id>' }), formService);
component.table = table;
component.table.field = new FormFieldModel(form, {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
readOnly: 'false',
restUrl: 'fake-rest-url'
});
component.column.optionType = 'rest';
component.table.field.isVisible = true;
getRestFieldValuesColumnByProcessId = spyOn(processDefinitionService, 'getRestFieldValuesColumnByProcessId').and.returnValue(
of(column.options)
);
});
afterEach(() => {
fixture.destroy();
});
it('should show visible dropdown widget', async () => {
const select = await loader.getHarness(MatSelectHarness.with({ selector: '#dropdown' }));
await select.open();
describe('and dropdown is populated via taskId', () => {
beforeEach(() => {
row = {value: {dropdown: 'one'}} as DynamicTableRow;
column = {
id: 'column-id',
optionType: 'rest',
options: [
{id: '1', name: 'one'},
{id: '2', name: 'two'}
]
} as DynamicTableColumn;
form = new FormModel({taskId: '<task-id>'});
dynamicTable = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'}), formService);
dynamicTable.rows.push(row);
dynamicTable.columns.push(column);
dropDownEditorComponent.table = dynamicTable;
dropDownEditorComponent.column = column;
dropDownEditorComponent.row = row;
dropDownEditorComponent.table.field = new FormFieldModel(form, {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
readOnly: 'false',
restUrl: 'fake-rest-url'
});
dropDownEditorComponent.table.field.isVisible = true;
fixture.detectChanges();
});
it('should show visible dropdown widget', () => {
expect(element.querySelector('#column-id')).toBeDefined();
expect(element.querySelector('#column-id')).not.toBeNull();
openSelect();
const optOne = fixture.debugElement.queryAll(By.css('[id="mat-option-1"]'));
const optTwo = fixture.debugElement.queryAll(By.css('[id="mat-option-2"]'));
const optThree = fixture.debugElement.queryAll(By.css('[id="mat-option-3"]'));
expect(optOne).not.toBeNull();
expect(optTwo).not.toBeNull();
expect(optThree).not.toBeNull();
});
});
describe('and dropdown is populated via processDefinitionId', () => {
beforeEach(() => {
row = {value: {dropdown: 'one'}} as DynamicTableRow;
column = {
id: 'column-id',
optionType: 'rest',
options: [
{id: '1', name: 'one'},
{id: '2', name: 'two'}
]
} as DynamicTableColumn;
form = new FormModel({processDefinitionId: '<proc-id>'});
dynamicTable = new DynamicTableModel(new FormFieldModel(form, {id: '<field-id>'}), formService);
dynamicTable.rows.push(row);
dynamicTable.columns.push(column);
dropDownEditorComponent.table = dynamicTable;
dropDownEditorComponent.column = column;
dropDownEditorComponent.row = row;
dropDownEditorComponent.table.field = new FormFieldModel(form, {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown',
readOnly: 'false',
restUrl: 'fake-rest-url'
});
dropDownEditorComponent.table.field.isVisible = true;
fixture.detectChanges();
});
it('should show visible dropdown widget', () => {
expect(element.querySelector('#column-id')).toBeDefined();
expect(element.querySelector('#column-id')).not.toBeNull();
openSelect();
const optOne = fixture.debugElement.queryAll(By.css('[id="mat-option-1"]'));
const optTwo = fixture.debugElement.queryAll(By.css('[id="mat-option-2"]'));
const optThree = fixture.debugElement.queryAll(By.css('[id="mat-option-3"]'));
expect(optOne).not.toBeNull();
expect(optTwo).not.toBeNull();
expect(optThree).not.toBeNull();
});
const options = await select.getOptions();
expect(getRestFieldValuesColumnByProcessId).toHaveBeenCalled();
expect(component.options.length).toBe(2);
expect(options.length).toBe(3);
});
});
});

View File

@@ -17,15 +17,7 @@
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { Observable, of } from 'rxjs';
import {
FormService,
ContainerModel,
FormFieldTypes,
FormFieldOption,
FormFieldModel,
FormModel,
CoreTestingModule
} from '@alfresco/adf-core';
import { FormService, ContainerModel, FormFieldTypes, FormFieldOption, FormFieldModel, FormModel, CoreTestingModule } from '@alfresco/adf-core';
import { RadioButtonsWidgetComponent } from './radio-buttons.widget';
import { MatIconModule } from '@angular/material/icon';
import { MatRadioModule } from '@angular/material/radio';
@@ -33,9 +25,12 @@ import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { TaskFormService } from '../../services/task-form.service';
import { ProcessDefinitionService } from '../../services/process-definition.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatRadioButtonHarness, MatRadioGroupHarness } from '@angular/material/radio/testing';
import { MatTooltipHarness } from '@angular/material/tooltip/testing';
describe('RadioButtonsWidgetComponent', () => {
let formService: FormService;
let widget: RadioButtonsWidgetComponent;
let taskFormService: TaskFormService;
@@ -43,20 +38,14 @@ describe('RadioButtonsWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
MatRadioModule,
FormsModule,
MatIconModule
]
imports: [TranslateModule.forRoot(), CoreTestingModule, MatRadioModule, FormsModule, MatIconModule]
});
taskFormService = TestBed.inject(TaskFormService);
processDefinitionService = TestBed.inject(ProcessDefinitionService);
formService = new FormService();
widget = new RadioButtonsWidgetComponent(formService, taskFormService, processDefinitionService, null);
widget.field = new FormFieldModel(new FormModel(), {restUrl: '<url>'});
widget.field = new FormFieldModel(new FormModel(), { restUrl: '<url>' });
});
it('should request field values from service', () => {
@@ -72,10 +61,12 @@ describe('RadioButtonsWidgetComponent', () => {
restUrl: '<url>'
});
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(new Observable((observer) => {
observer.next(null);
observer.complete();
}));
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(
new Observable((observer) => {
observer.next(null);
observer.complete();
})
);
widget.ngOnInit();
expect(taskFormService.getRestFieldValues).toHaveBeenCalledWith(taskId, fieldId);
});
@@ -95,10 +86,12 @@ describe('RadioButtonsWidgetComponent', () => {
const field = widget.field;
spyOn(field, 'updateForm').and.stub();
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(new Observable((observer) => {
observer.next(null);
observer.complete();
}));
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(
new Observable((observer) => {
observer.next(null);
observer.complete();
})
);
widget.ngOnInit();
expect(field.updateForm).toHaveBeenCalled();
});
@@ -115,10 +108,12 @@ describe('RadioButtonsWidgetComponent', () => {
id: fieldId,
restUrl: '<url>'
});
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(new Observable((observer) => {
observer.next(null);
observer.complete();
}));
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(
new Observable((observer) => {
observer.next(null);
observer.complete();
})
);
const field = widget.field;
widget.field = null;
@@ -146,6 +141,8 @@ describe('RadioButtonsWidgetComponent', () => {
let radioButtonWidget: RadioButtonsWidgetComponent;
let fixture: ComponentFixture<RadioButtonsWidgetComponent>;
let element: HTMLElement;
let loader: HarnessLoader;
const restOption: FormFieldOption[] = [
{
id: 'opt-1',
@@ -154,12 +151,14 @@ describe('RadioButtonsWidgetComponent', () => {
{
id: 'opt-2',
name: 'opt-name-2'
}];
}
];
beforeEach(() => {
fixture = TestBed.createComponent(RadioButtonsWidgetComponent);
radioButtonWidget = fixture.componentInstance;
element = fixture.nativeElement;
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should show radio buttons as text when is readonly', async () => {
@@ -170,8 +169,7 @@ describe('RadioButtonsWidgetComponent', () => {
readOnly: true
});
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
expect(element.querySelector('display-text-widget')).toBeDefined();
});
@@ -197,26 +195,21 @@ describe('RadioButtonsWidgetComponent', () => {
options: restOption,
restUrl: null
});
fixture.detectChanges();
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const widgetLabel = element.querySelector('label');
expect(widgetLabel.innerText).toBe('radio-name-label*');
expect(radioButtonWidget.field.isValid).toBe(false);
const option = element.querySelector<HTMLElement>('#radio-id-opt-1 label');
option.click();
const option = await loader.getHarness(MatRadioButtonHarness.with({ selector: '#radio-id-opt-1' }));
await option.check();
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const selectedOption = element.querySelector<HTMLElement>('[class*="mat-radio-checked"]');
expect(selectedOption.innerText).toBe('opt-name-1');
const selectedOption = await loader.getHarness(MatRadioButtonHarness.with({ checked: true }));
expect(await selectedOption.getLabelText()).toBe('opt-name-1');
expect(radioButtonWidget.field.isValid).toBe(true);
});
it('should be able to set another Radio Button widget as required', () => {
it('should be able to set another Radio Button widget as required', async () => {
radioButtonWidget.field = new FormFieldModel(new FormModel({}), {
id: 'radio-id',
name: 'radio-name-label',
@@ -228,10 +221,10 @@ describe('RadioButtonsWidgetComponent', () => {
restUrl: null,
value: 'opt-name-2'
});
fixture.detectChanges();
const selectedOption = element.querySelector<HTMLElement>('[class*="mat-radio-checked"]');
expect(selectedOption.innerText).toBe('opt-name-2');
const selectedOption = await loader.getHarness(MatRadioButtonHarness.with({ checked: true }));
expect(await selectedOption.getLabelText()).toBe('opt-name-2');
expect(radioButtonWidget.field.isValid).toBe(true);
});
@@ -249,19 +242,16 @@ describe('RadioButtonsWidgetComponent', () => {
});
fixture.detectChanges();
await fixture.whenStable();
const radioButtonsElement: any = element.querySelector('#radio-id-opt-1');
const tooltip = radioButtonsElement.getAttribute('ng-reflect-message');
expect(tooltip).toEqual(radioButtonWidget.field.tooltip);
const tooltip = await loader.getHarness(MatTooltipHarness.with({ selector: '#radio-id-opt-1' }));
await tooltip.show();
expect(await tooltip.getTooltipText()).toEqual(radioButtonWidget.field.tooltip);
});
describe('and radioButton is populated via taskId', () => {
beforeEach(() => {
spyOn(taskFormService, 'getRestFieldValues').and.returnValue(of(restOption));
radioButtonWidget.field = new FormFieldModel(new FormModel({taskId: 'task-id'}), {
radioButtonWidget.field = new FormFieldModel(new FormModel({ taskId: 'task-id' }), {
id: 'radio-id',
name: 'radio-name',
type: FormFieldTypes.RADIO_BUTTONS,
@@ -293,37 +283,38 @@ describe('RadioButtonsWidgetComponent', () => {
}));
describe('and radioButton is readonly', () => {
beforeEach(() => {
radioButtonWidget.field.readOnly = true;
fixture.detectChanges();
});
it('should show radio buttons disabled', () => {
expect(element.querySelector('.mat-radio-disabled[ng-reflect-id="radio-id-opt-1"]')).toBeDefined();
expect(element.querySelector('.mat-radio-disabled[ng-reflect-id="radio-id-opt-1"]')).not.toBeNull();
expect(element.querySelector('.mat-radio-disabled[ng-reflect-id="radio-id-opt-2"]')).toBeDefined();
expect(element.querySelector('.mat-radio-disabled[ng-reflect-id="radio-id-opt-2"]')).not.toBeNull();
it('should show radio buttons disabled', async () => {
const radioButtons = await (
await loader.getHarness(MatRadioGroupHarness.with({ selector: '.adf-radio-group' }))
).getRadioButtons();
expect(await radioButtons[0].isDisabled()).toBe(true);
expect(await radioButtons[1].isDisabled()).toBe(true);
});
describe('and a value is selected', () => {
beforeEach(() => {
radioButtonWidget.field.value = restOption[0].id;
fixture.detectChanges();
});
it('should check the selected value', () => {
expect(element.querySelector('.mat-radio-checked')).toBe(element.querySelector('mat-radio-button[ng-reflect-id="radio-id-opt-1"]'));
it('should check the selected value', async () => {
const checkedRadioButton = await (
await loader.getHarness(MatRadioGroupHarness.with({ selector: '.adf-radio-group' }))
).getCheckedRadioButton();
expect(await checkedRadioButton.getLabelText()).toBe(restOption[0].name);
});
});
});
});
describe('and radioButton is populated via processDefinitionId', () => {
beforeEach(() => {
radioButtonWidget.field = new FormFieldModel(new FormModel({processDefinitionId: 'proc-id'}), {
radioButtonWidget.field = new FormFieldModel(new FormModel({ processDefinitionId: 'proc-id' }), {
id: 'radio-id',
name: 'radio-name',
type: FormFieldTypes.RADIO_BUTTONS,

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { DebugElement, NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
@@ -28,12 +28,16 @@ import { ProcessInstanceDetailsComponent } from './process-instance-details.comp
import { ProcessTestingModule } from '../../testing/process.testing.module';
import { FormModule } from '../../form';
import { TranslateModule } from '@ngx-translate/core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatCardHarness } from '@angular/material/card/testing';
describe('ProcessInstanceDetailsComponent', () => {
let service: ProcessService;
let component: ProcessInstanceDetailsComponent;
let fixture: ComponentFixture<ProcessInstanceDetailsComponent>;
let loader: HarnessLoader;
let getProcessSpy: jasmine.Spy;
beforeEach(() => {
@@ -48,6 +52,7 @@ describe('ProcessInstanceDetailsComponent', () => {
});
fixture = TestBed.createComponent(ProcessInstanceDetailsComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
service = fixture.debugElement.injector.get(ProcessService);
const commentService = fixture.debugElement.injector.get(CommentProcessService);
@@ -76,12 +81,8 @@ describe('ProcessInstanceDetailsComponent', () => {
fixture.detectChanges();
component.ngOnChanges({ processInstanceId: new SimpleChange(null, '123', true) });
fixture.detectChanges();
await fixture.whenStable();
const headerEl: DebugElement = fixture.debugElement.query(By.css('.mat-card-title '));
expect(headerEl).not.toBeNull();
expect(headerEl.nativeElement.innerText).toBe('Process 123');
const headerEl = await loader.getHarness(MatCardHarness);
expect(await headerEl.getTitleText()).toBe('Process 123');
});
it('should display default details when the process instance has no name', async () => {
@@ -90,12 +91,8 @@ describe('ProcessInstanceDetailsComponent', () => {
fixture.detectChanges();
component.ngOnChanges({ processInstanceId: new SimpleChange(null, '123', true) });
fixture.detectChanges();
await fixture.whenStable();
const headerEl: DebugElement = fixture.debugElement.query(By.css('.mat-card-title '));
expect(headerEl).not.toBeNull();
expect(headerEl.nativeElement.innerText).toBe('My Process - Nov 10, 2016, 3:37:30 AM');
const headerEl = await loader.getHarness(MatCardHarness);
expect(await headerEl.getTitleText()).toBe('My Process - Nov 10, 2016, 3:37:30 AM');
});
it('should enable diagram button if the process is running', async () => {

View File

@@ -26,48 +26,42 @@ import { ProcessService } from './../services/process.service';
import { ProcessInstanceTasksComponent } from './process-instance-tasks.component';
import { ProcessTestingModule } from '../../testing/process.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatListItemHarness } from '@angular/material/list/testing';
describe('ProcessInstanceTasksComponent', () => {
let component: ProcessInstanceTasksComponent;
let fixture: ComponentFixture<ProcessInstanceTasksComponent>;
let service: ProcessService;
// let getProcessTasksSpy: jasmine.Spy;
let loader: HarnessLoader;
let processService: ProcessService;
const exampleProcessInstance = new ProcessInstance({ id: '123' });
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
]
imports: [TranslateModule.forRoot(), ProcessTestingModule]
});
fixture = TestBed.createComponent(ProcessInstanceTasksComponent);
processService = TestBed.inject(ProcessService);
component = fixture.componentInstance;
service = TestBed.inject(ProcessService);
loader = TestbedHarnessEnvironment.loader(fixture);
spyOn(service, 'getProcessTasks').and.returnValue(of([new TaskDetailsModel(taskDetailsMock)]));
spyOn(processService, 'getProcessTasks').and.returnValue(of([new TaskDetailsModel(taskDetailsMock)]));
});
afterEach(() => {
fixture.destroy();
});
it('should initially render message about no active tasks if no process instance ID provided', async () => {
it('should initially render message about no active tasks if no process instance ID provided', () => {
component.processInstanceDetails = undefined;
fixture.detectChanges();
await fixture.whenStable();
const msgEl = fixture.debugElement.query(By.css('[data-automation-id="active-tasks-none"]'));
expect(msgEl).not.toBeNull();
});
it('should initially render message about no completed tasks if no process instance ID provided', async () => {
it('should initially render message about no completed tasks if no process instance ID provided', () => {
component.processInstanceDetails = undefined;
fixture.detectChanges();
await fixture.whenStable();
const msgEl = fixture.debugElement.query(By.css('[data-automation-id="completed-tasks-none"]'));
expect(msgEl).not.toBeNull();
@@ -92,13 +86,8 @@ describe('ProcessInstanceTasksComponent', () => {
fixture.detectChanges();
component.ngOnChanges({ processInstanceDetails: change });
fixture.detectChanges();
await fixture.whenStable();
component.ngOnChanges({ processInstanceDetails: change });
const listEl = fixture.debugElement.query(By.css('[data-automation-id="active-tasks"]'));
expect(listEl).not.toBeNull();
expect(listEl.queryAll(By.css('mat-list-item')).length).toBe(1);
const items = await loader.getAllHarnesses(MatListItemHarness.with({ ancestor: '[data-automation-id="active-tasks"]' }));
expect(items.length).toBe(1);
});
it('should display completed tasks', async () => {
@@ -106,51 +95,42 @@ describe('ProcessInstanceTasksComponent', () => {
fixture.detectChanges();
component.ngOnChanges({ processInstanceDetails: change });
fixture.detectChanges();
await fixture.whenStable();
const listEl = fixture.debugElement.query(By.css('[data-automation-id="completed-tasks"]'));
expect(listEl).not.toBeNull();
expect(listEl.queryAll(By.css('mat-list-item')).length).toBe(1);
const items = await loader.getAllHarnesses(MatListItemHarness.with({ ancestor: '[data-automation-id="completed-tasks"]' }));
expect(items.length).toBe(1);
});
describe('task reloading', () => {
beforeEach(() => {
component.processInstanceDetails = exampleProcessInstance;
});
it('should render a refresh button by default', async () => {
it('should render a refresh button by default', () => {
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.debugElement.query(By.css('.process-tasks-refresh'))).not.toBeNull();
});
it('should render a refresh button if configured to', async () => {
it('should render a refresh button if configured to', () => {
component.showRefreshButton = true;
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.debugElement.query(By.css('.process-tasks-refresh'))).not.toBeNull();
});
it('should NOT render a refresh button if configured not to', async () => {
it('should NOT render a refresh button if configured not to', () => {
component.showRefreshButton = false;
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.debugElement.query(By.css('.process-tasks-refresh'))).toBeNull();
});
it('should call service to get tasks when reload button clicked', async () => {
it('should call service to get tasks when reload button clicked', () => {
fixture.detectChanges();
await fixture.whenStable();
component.onRefreshClicked();
expect(service.getProcessTasks).toHaveBeenCalled();
expect(processService.getProcessTasks).toHaveBeenCalled();
});
});
});

View File

@@ -33,11 +33,16 @@ import {
import { ProcessService } from '../services/process.service';
import { ProcessTestingModule } from '../../testing/process.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { MatMenuItemHarness } from '@angular/material/menu/testing';
describe('ProcessInstanceListComponent', () => {
let fixture: ComponentFixture<ProcessInstanceListComponent>;
let component: ProcessInstanceListComponent;
let loader: HarnessLoader;
let service: ProcessService;
let getProcessInstancesSpy: jasmine.Spy;
let appConfig: AppConfigService;
@@ -59,6 +64,7 @@ describe('ProcessInstanceListComponent', () => {
});
fixture = TestBed.createComponent(ProcessInstanceListComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
appConfig = TestBed.inject(AppConfigService);
service = TestBed.inject(ProcessService);
@@ -68,11 +74,9 @@ describe('ProcessInstanceListComponent', () => {
};
});
it('should display loading spinner', () => {
it('should display loading spinner', async () => {
component.isLoading = true;
const spinner = fixture.debugElement.query(By.css('.mat-progress-spinner'));
expect(spinner).toBeDefined();
await loader.getHarness(MatProgressSpinnerHarness);
});
it('should use the default schemaColumn as default', () => {
@@ -619,6 +623,7 @@ describe('ProcessListContextMenuComponent', () => {
let customComponent: ProcessListContextMenuComponent;
let processService: ProcessService;
let element: HTMLElement;
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -631,6 +636,7 @@ describe('ProcessListContextMenuComponent', () => {
fixture = TestBed.createComponent(ProcessListContextMenuComponent);
customComponent = fixture.componentInstance;
element = fixture.nativeElement;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
processService = TestBed.inject(ProcessService);
customComponent.appId = 12345;
spyOn(processService, 'getProcesses').and.returnValue(of(fakeProcessInstance));
@@ -645,15 +651,13 @@ describe('ProcessListContextMenuComponent', () => {
const contextMenu = element.querySelector(`[data-automation-id="text_${fakeProcessInstance.data[0].name}"]`);
const contextActionSpy = spyOn(customComponent.contextAction, 'emit').and.callThrough();
contextMenu.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }));
fixture.detectChanges();
await fixture.whenStable();
const contextActions = document.querySelectorAll('.mat-menu-item');
const contextActions = await loader.getAllHarnesses(MatMenuItemHarness);
expect(contextActions.length).toBe(2);
expect(contextActions[0]['disabled']).toBe(false, 'View Process Details action not enabled');
expect(contextActions[1]['disabled']).toBe(false, 'Cancel Process action not enabled');
contextActions[0].dispatchEvent(new Event('click'));
fixture.detectChanges();
expect(await contextActions[0].isDisabled()).toBe(false, 'View Process Details action not enabled');
expect(await contextActions[1].isDisabled()).toBe(false, 'Cancel Process action not enabled');
await contextActions[0].click();
expect(contextActionSpy).toHaveBeenCalled();
});
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { DebugElement, SimpleChange } from '@angular/core';
import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppConfigService } from '@alfresco/adf-core';
import { AppsProcessService } from '../../app-list/services/apps-process.service';
@@ -26,18 +26,22 @@ import { ProcessService } from '../services/process.service';
import { newProcess, taskFormMock, testProcessDef, testMultipleProcessDefs, testProcessDefWithForm, testProcessDefinitions } from '../../mock';
import { StartProcessInstanceComponent } from './start-process.component';
import { ProcessTestingModule } from '../../testing/process.testing.module';
import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
import { deployedApps } from '../../mock/apps-list.mock';
import { ProcessNamePipe } from '../../pipes/process-name.pipe';
import { ProcessInstance } from '../models/process-instance.model';
import { ActivitiContentService } from '../../form/services/activiti-alfresco.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { MatFormFieldHarness } from '@angular/material/form-field/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatAutocompleteHarness } from '@angular/material/autocomplete/testing';
describe('StartProcessComponent', () => {
let appConfig: AppConfigService;
let activitiContentService: ActivitiContentService;
let component: StartProcessInstanceComponent;
let fixture: ComponentFixture<StartProcessInstanceComponent>;
let loader: HarnessLoader;
let processService: ProcessService;
let appsProcessService: AppsProcessService;
let getDefinitionsSpy: jasmine.Spy;
@@ -52,16 +56,13 @@ describe('StartProcessComponent', () => {
});
});
const selectOptionByName = (name: string) => {
const selectOptionByName = async (name: string) => {
const selectElement = fixture.nativeElement.querySelector('button#adf-select-process-dropdown');
selectElement.click();
fixture.detectChanges();
const options: any = fixture.debugElement.queryAll(By.css('.mat-option-text'));
const currentOption = options.find((option: DebugElement) => option.nativeElement.innerHTML.trim() === name);
if (currentOption) {
currentOption.nativeElement.click();
}
const autocompleteDropdown = await loader.getHarness(MatAutocompleteHarness);
const options = await autocompleteDropdown.getOptions({ text: name });
expect(options.length).toBe(1);
await options[0].click();
};
/**
@@ -80,6 +81,7 @@ describe('StartProcessComponent', () => {
activitiContentService = TestBed.inject(ActivitiContentService);
fixture = TestBed.createComponent(StartProcessInstanceComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
processService = TestBed.inject(ProcessService);
appsProcessService = TestBed.inject(AppsProcessService);
@@ -175,11 +177,8 @@ describe('StartProcessComponent', () => {
component.processDefinitionInput.setValue('My Default Name');
component.processNameInput.setValue('claim');
fixture.detectChanges();
await fixture.whenStable();
const inputLabelsNodes = document.querySelectorAll('.adf-start-process .adf-process-input-container mat-label');
expect(inputLabelsNodes.length).toBe(2);
const inputLabels = await loader.getAllHarnesses(MatFormFieldHarness.with({ ancestor: '.adf-start-process', selector: '.adf-process-input-container' }));
expect(inputLabels.length).toBe(2);
});
it('should have floating labels for process name and type', async () => {
@@ -313,13 +312,10 @@ describe('StartProcessComponent', () => {
const selectElement = fixture.nativeElement.querySelector('button#adf-select-process-dropdown');
selectElement.click();
fixture.detectChanges();
await fixture.whenStable();
const options: any = fixture.debugElement.queryAll(By.css('.mat-option-text'));
const options = await (await loader.getHarness(MatAutocompleteHarness)).getOptions();
expect(options.length).toBe(2);
expect(options[0].nativeElement.innerText).toBe('My Process 1');
expect(options[1].nativeElement.innerText).toBe('My Process 2');
expect(await options[0].getText()).toBe('My Process 1');
expect(await options[1].getText()).toBe('My Process 2');
});
it('should show no process available message when no process definition is loaded', async () => {
@@ -511,24 +507,21 @@ describe('StartProcessComponent', () => {
expect(startProcessEmitterSpy).toHaveBeenCalledWith(newProcess);
});
it('should emit processDefinitionSelection event when a process definition is selected', () => {
it('should emit processDefinitionSelection event when a process definition is selected', async () => {
const processDefinitionSelectionSpy = spyOn(component.processDefinitionSelection, 'emit');
fixture.detectChanges();
selectOptionByName(testProcessDef.name);
await selectOptionByName(testProcessDef.name);
expect(processDefinitionSelectionSpy).toHaveBeenCalledWith(testProcessDef);
});
it('should set the process name using the processName pipe when a process definition gets selected', () => {
it('should set the process name using the processName pipe when a process definition gets selected', async () => {
const processNamePipe = TestBed.inject(ProcessNamePipe);
const processNamePipeTransformSpy = spyOn(processNamePipe, 'transform').and.returnValue('fake-transformed-name');
const expectedProcessInstanceDetails = new ProcessInstance({ processDefinitionName: testProcessDef.name });
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(testMultipleProcessDefs));
changeAppId(123);
fixture.detectChanges();
selectOptionByName(testProcessDef.name);
await selectOptionByName(testProcessDef.name);
expect(processNamePipeTransformSpy).toHaveBeenCalledWith(component.name, expectedProcessInstanceDetails);
expect(component.nameController.dirty).toBe(true);
@@ -570,16 +563,16 @@ describe('StartProcessComponent', () => {
getDeployedApplicationsSpy = spyOn(appsProcessService, 'getDeployedApplications').and.returnValue(of(deployedApps));
});
it('Should be able to show application drop-down if showSelectApplicationDropdown set to true', () => {
it('Should be able to show application drop-down if showSelectApplicationDropdown set to true', async () => {
getDefinitionsSpy.and.returnValue(of(testMultipleProcessDefs));
changeAppId(3);
fixture.detectChanges();
const appsSelector = fixture.nativeElement.querySelector('[data-automation-id="adf-start-process-apps-drop-down"]');
const labelElement = fixture.nativeElement.querySelector('.adf-start-process-app-list .mat-form-field-label');
const labelText = await (await loader.getHarness(MatFormFieldHarness.with({ selector: '.adf-start-process-app-list' }))).getLabel();
expect(appsSelector).not.toBeNull();
expect(labelElement.innerText).toEqual('ADF_PROCESS_LIST.START_PROCESS.FORM.LABEL.SELECT_APPLICATION');
expect(labelText).toEqual('ADF_PROCESS_LIST.START_PROCESS.FORM.LABEL.SELECT_APPLICATION');
expect(getDeployedApplicationsSpy).toHaveBeenCalled();
expect(component.applications.length).toBe(6);

View File

@@ -22,15 +22,18 @@ import { MatMenuModule } from '@angular/material/menu';
import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
import { BpmUserModel } from '../common/models/bpm-user.model';
import { ProcessUserInfoComponent } from './process-user-info.component';
import { fakeBpmUser } from './mocks/bpm-user.service.mock';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatTabGroupHarness, MatTabHarness } from '@angular/material/tabs/testing';
describe('ProcessUserInfoComponent', () => {
const profilePictureUrl = 'alfresco-logo.svg';
let component: ProcessUserInfoComponent;
let fixture: ComponentFixture<ProcessUserInfoComponent>;
let loader: HarnessLoader;
let element: HTMLElement;
const openUserInfo = () => {
@@ -56,6 +59,7 @@ describe('ProcessUserInfoComponent', () => {
});
fixture = TestBed.createComponent(ProcessUserInfoComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
element = fixture.nativeElement;
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
@@ -120,9 +124,8 @@ describe('ProcessUserInfoComponent', () => {
it('should not show the tabs', async () => {
await whenFixtureReady();
openUserInfo();
await fixture.whenStable();
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('#tab-group-env')).classes['adf-hide-tab']).toBeTruthy();
const tabGroupHost = await (await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }))).host();
expect(await tabGroupHost.hasClass('adf-hide-tab')).toBeTruthy();
});
});
@@ -138,19 +141,16 @@ describe('ProcessUserInfoComponent', () => {
it('should show the tabs', async () => {
await whenFixtureReady();
openUserInfo();
await fixture.whenStable();
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('#tab-group-env')).classes['adf-hide-tab']).toBeFalsy();
const tabGroupHost = await (await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }))).host();
expect(await tabGroupHost.hasClass('adf-hide-tab')).toBeFalsy();
});
it('should get the bpm user information', async () => {
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
const bpmTab = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'))[1];
bpmTab.triggerEventHandler('click', null);
fixture.detectChanges();
await fixture.whenStable();
const bpmTab = await loader.getHarness(MatTabHarness.with({ label: 'USER_PROFILE.TAB.PS' }));
await bpmTab.select();
const bpmUsername = fixture.debugElement.query(By.css('#bpm-username'));
const bpmImage = fixture.debugElement.query(By.css('#bpm-user-detail-image'));
expect(element.querySelector('#userinfo_container')).not.toBeNull();
@@ -198,24 +198,19 @@ describe('ProcessUserInfoComponent', () => {
it('should show the tabs for the env', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroup = fixture.debugElement.query(By.css('#tab-group-env'));
const tabs = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'));
const tabGroup = await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }));
const tabs = await tabGroup.getTabs();
expect(tabGroup).not.toBeNull();
expect(tabGroup.classes['adf-hide-tab']).toBeFalsy();
expect(await (await tabGroup.host()).hasClass('adf-hide-tab')).toBeFalsy();
expect(tabs.length).toBe(2);
});
it('should not close the menu when a tab is clicked', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroup = fixture.debugElement.query(By.css('#tab-group-env'));
const bpmTab = await loader.getHarness(MatTabHarness.with({ label: 'USER_PROFILE.TAB.PS' }));
const tabs = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'));
expect(tabGroup).not.toBeNull();
tabs[1].triggerEventHandler('click', null);
fixture.detectChanges();
bpmTab.select();
expect(fixture.debugElement.query(By.css('#user-profile-lists'))).not.toBeNull();
});
});

View File

@@ -24,11 +24,14 @@ import { ProcessTestingModule } from '../../testing/process.testing.module';
import { taskDetailsMock } from '../../mock/task/task-details.mock';
import { TaskDetailsModel } from '../models/task-details.model';
import { TranslateModule } from '@ngx-translate/core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatButtonHarness } from '@angular/material/button/testing';
describe('StartTaskComponent', () => {
let component: StartTaskComponent;
let fixture: ComponentFixture<StartTaskComponent>;
let loader: HarnessLoader;
let service: TaskListService;
let logService: LogService;
let element: HTMLElement;
@@ -51,14 +54,12 @@ describe('StartTaskComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
]
imports: [TranslateModule.forRoot(), ProcessTestingModule]
});
fixture = TestBed.createComponent(StartTaskComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
loader = TestbedHarnessEnvironment.loader(fixture);
service = TestBed.inject(TaskListService);
logService = TestBed.inject(LogService);
@@ -81,16 +82,15 @@ describe('StartTaskComponent', () => {
});
describe('create task', () => {
beforeEach(() => {
createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(of(
{
createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: null,
assignee: null
} as any
));
} as any)
);
});
it('should create new task when start is clicked', () => {
@@ -140,25 +140,25 @@ describe('StartTaskComponent', () => {
describe('attach form', () => {
beforeEach(() => {
spyOn(service, 'createNewTask').and.returnValue(of(
{
spyOn(service, 'createNewTask').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: null,
assignee: null
} as any
));
} as any)
);
});
it('should attach form to the task when a form is selected', () => {
spyOn(service, 'attachFormToATask').and.returnValue(of(
{
spyOn(service, 'attachFormToATask').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: 1204,
assignee: null
}
));
})
);
const successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(1204);
@@ -176,14 +176,14 @@ describe('StartTaskComponent', () => {
});
it('should not attach form to the task when a no form is selected', () => {
spyOn(service, 'attachFormToATask').and.returnValue(of(
{
spyOn(service, 'attachFormToATask').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: null,
assignee: null
}
));
})
);
const successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(null);
@@ -203,30 +203,30 @@ describe('StartTaskComponent', () => {
describe('assign user', () => {
beforeEach(() => {
spyOn(service, 'createNewTask').and.returnValue(of(
{
spyOn(service, 'createNewTask').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: null,
assignee: null
} as any
));
spyOn(service, 'attachFormToATask').and.returnValue(of(
{
} as any)
);
spyOn(service, 'attachFormToATask').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: 1204,
assignee: null
}
));
spyOn(service, 'assignTaskByUserId').and.returnValue(of(
{
})
);
spyOn(service, 'assignTaskByUserId').and.returnValue(
of({
id: 91,
name: 'fakeName',
formKey: 1204,
assignee: testUser
} as any
));
} as any)
);
});
it('should assign task when an assignee is selected', () => {
@@ -285,7 +285,7 @@ describe('StartTaskComponent', () => {
it('should not attach a form when a form id is not selected', () => {
const attachFormToATask = spyOn(service, 'attachFormToATask').and.returnValue(of([]));
spyOn(service, 'createNewTask').and.returnValue(of(new TaskDetailsModel({ id: 'task-id'})));
spyOn(service, 'createNewTask').and.returnValue(of(new TaskDetailsModel({ id: 'task-id' })));
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
const createTaskButton = element.querySelector<HTMLElement>('#button-start');
@@ -301,9 +301,11 @@ describe('StartTaskComponent', () => {
expect(element.querySelector('#button-start').textContent).toContain('ADF_TASK_LIST.START_TASK.FORM.ACTION.START');
});
it('should render start task button with primary color', () => {
it('should render start task button with primary color', async () => {
fixture.detectChanges();
expect(element.querySelector('#button-start').classList.contains('mat-primary')).toBeTruthy();
const buttonEl = await (await loader.getHarness(MatButtonHarness.with({ selector: '#button-start' }))).host();
expect(await buttonEl.getAttribute('color')).toBe('primary');
});
it('should render task buttons with uppercase text', () => {

View File

@@ -21,13 +21,7 @@ import { By } from '@angular/platform-browser';
import { of, throwError } from 'rxjs';
import { FormModel, FormOutcomeEvent, FormOutcomeModel, CommentModel, User } from '@alfresco/adf-core';
import { TaskDetailsModel } from '../models/task-details.model';
import {
noDataMock,
taskDetailsMock,
taskFormMock,
tasksMock,
taskDetailsWithOutAssigneeMock
} from '../../mock';
import { noDataMock, taskDetailsMock, taskFormMock, tasksMock, taskDetailsWithOutAssigneeMock } from '../../mock';
import { TaskListService } from './../services/tasklist.service';
import { TaskDetailsComponent } from './task-details.component';
import { ProcessTestingModule } from '../../testing/process.testing.module';
@@ -37,6 +31,9 @@ import { TaskFormService } from '../../form/services/task-form.service';
import { TaskCommentsService } from '../../task-comments/services/task-comments.service';
import { UserProcessModel } from '../../common/models/user-process.model';
import { PeopleProcessService } from '../../common/services/people-process.service';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HarnessLoader } from '@angular/cdk/testing';
import { MatDialogHarness } from '@angular/material/dialog/testing';
const fakeUser = new UserProcessModel({
id: 'fake-id',
@@ -58,6 +55,7 @@ describe('TaskDetailsComponent', () => {
let taskFormService: TaskFormService;
let component: TaskDetailsComponent;
let fixture: ComponentFixture<TaskDetailsComponent>;
let loader: HarnessLoader;
let getTaskDetailsSpy: jasmine.Spy;
let getCommentsSpy: jasmine.Spy;
let getTasksSpy: jasmine.Spy;
@@ -67,10 +65,7 @@ describe('TaskDetailsComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
],
imports: [TranslateModule.forRoot(), ProcessTestingModule],
schemas: [NO_ERRORS_SCHEMA]
});
peopleProcessService = TestBed.inject(PeopleProcessService);
@@ -92,15 +87,18 @@ describe('TaskDetailsComponent', () => {
assignTaskSpy = spyOn(taskListService, 'assignTask').and.returnValue(of(fakeTaskAssignResponse));
taskCommentsService = TestBed.inject(TaskCommentsService);
getCommentsSpy = spyOn(taskCommentsService, 'get').and.returnValue(of([
new CommentModel({ message: 'Test1', created: new Date(), createdBy: new User({ firstName: 'Admin', lastName: 'User' }) }),
new CommentModel({ message: 'Test2', created: new Date(), createdBy: new User({ firstName: 'Admin', lastName: 'User' }) }),
new CommentModel({ message: 'Test3', created: new Date(), createdBy: new User({ firstName: 'Admin', lastName: 'User' }) })
]));
getCommentsSpy = spyOn(taskCommentsService, 'get').and.returnValue(
of([
new CommentModel({ message: 'Test1', created: new Date(), createdBy: new User({ firstName: 'Admin', lastName: 'User' }) }),
new CommentModel({ message: 'Test2', created: new Date(), createdBy: new User({ firstName: 'Admin', lastName: 'User' }) }),
new CommentModel({ message: 'Test3', created: new Date(), createdBy: new User({ firstName: 'Admin', lastName: 'User' }) })
])
);
fixture = TestBed.createComponent(TaskDetailsComponent);
peopleProcessService = TestBed.inject(PeopleProcessService);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
});
afterEach(() => {
@@ -122,7 +120,7 @@ describe('TaskDetailsComponent', () => {
it('should send a claim task event when a task is claimed', () => {
let lastValue: string;
component.claimedTask.subscribe((taskId) => lastValue = taskId);
component.claimedTask.subscribe((taskId) => (lastValue = taskId));
component.onClaimAction('FAKE-TASK-CLAIM');
expect(lastValue).toBe('FAKE-TASK-CLAIM');
});
@@ -182,7 +180,6 @@ describe('TaskDetailsComponent', () => {
}));
describe('change detection', () => {
let change;
let nullChange;
@@ -209,7 +206,6 @@ describe('TaskDetailsComponent', () => {
});
describe('Form events', () => {
beforeEach(() => {
component.taskId = '123';
fixture.detectChanges();
@@ -289,15 +285,13 @@ describe('TaskDetailsComponent', () => {
expect(emitSpy).toHaveBeenCalled();
});
it('should display a dialog to the user when a form error occurs', () => {
let dialogEl = window.document.querySelector('mat-dialog-content');
it('should display a dialog to the user when a form error occurs', async () => {
const dialogEl = await loader.getHarnessOrNull(MatDialogHarness);
expect(dialogEl).toBeNull();
component.onFormError({});
fixture.detectChanges();
dialogEl = window.document.querySelector('mat-dialog-content');
expect(dialogEl).not.toBeNull();
await loader.getHarness(MatDialogHarness);
});
it('should emit a task created event when checklist task is created', () => {
@@ -306,10 +300,9 @@ describe('TaskDetailsComponent', () => {
component.onChecklistTaskCreated(mockTask);
expect(emitSpy).toHaveBeenCalled();
});
});
});
describe('Comments', () => {
it('should comments be readonly if the task is complete and no user are involved', () => {
component.showComments = true;
component.showHeaderContent = true;
@@ -381,27 +374,31 @@ describe('TaskDetailsComponent', () => {
});
describe('assign task to user', () => {
beforeEach(() => {
component.taskId = '123';
fixture.detectChanges();
});
it('should return an observable with user search results', () => {
spyOn(peopleProcessService, 'getWorkflowUsers').and.returnValue(of([{
id: 1,
firstName: 'fake-test-1',
lastName: 'fake-last-1',
email: 'fake-test-1@test.com'
}, {
id: 2,
firstName: 'fake-test-2',
lastName: 'fake-last-2',
email: 'fake-test-2@test.com'
}]));
spyOn(peopleProcessService, 'getWorkflowUsers').and.returnValue(
of([
{
id: 1,
firstName: 'fake-test-1',
lastName: 'fake-last-1',
email: 'fake-test-1@test.com'
},
{
id: 2,
firstName: 'fake-test-2',
lastName: 'fake-last-2',
email: 'fake-test-2@test.com'
}
])
);
let lastValue: UserProcessModel[];
component.peopleSearch.subscribe((users) => lastValue = users);
component.peopleSearch.subscribe((users) => (lastValue = users));
component.searchUser('fake-search-word');
expect(lastValue.length).toBe(2);
@@ -415,7 +412,7 @@ describe('TaskDetailsComponent', () => {
spyOn(peopleProcessService, 'getWorkflowUsers').and.returnValue(of([]));
let lastValue: UserProcessModel[];
component.peopleSearch.subscribe((users) => lastValue = users);
component.peopleSearch.subscribe((users) => (lastValue = users));
component.searchUser('fake-search-word');
expect(lastValue.length).toBe(0);

View File

@@ -30,12 +30,17 @@ import {
} from '../../mock';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { of, Subject } from 'rxjs';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HarnessLoader } from '@angular/cdk/testing';
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
import { MatMenuItemHarness } from '@angular/material/menu/testing';
declare let jasmine: any;
describe('TaskListComponent', () => {
let component: TaskListComponent;
let fixture: ComponentFixture<TaskListComponent>;
let loader: HarnessLoader;
let appConfig: AppConfigService;
let taskListService: TaskListService;
@@ -71,27 +76,19 @@ describe('TaskListComponent', () => {
component.selectionMode = selectionMode;
}
component.ngOnChanges({ sort: state });
fixture.detectChanges();
await fixture.whenStable();
const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container');
const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container');
selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask2.dispatchEvent(new MouseEvent('click', { bubbles: true }));
fixture.detectChanges();
await fixture.whenStable();
const selectTask1 = await loader.getHarness(MatCheckboxHarness.with({ ancestor: '[data-automation-id="datatable-row-0"]' }));
const selectTask2 = await loader.getHarness(MatCheckboxHarness.with({ ancestor: '[data-automation-id="datatable-row-1"]' }));
await selectTask1.toggle();
await selectTask1.toggle();
await selectTask2.toggle();
let selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
let selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeDefined();
expect(component.selectedInstances.length).toBe(2);
selectTask2.dispatchEvent(new MouseEvent('click', { bubbles: true }));
fixture.detectChanges();
await fixture.whenStable();
await selectTask2.toggle();
expect(component.selectedInstances.length).toBe(1);
selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
@@ -112,6 +109,7 @@ describe('TaskListComponent', () => {
fixture = TestBed.createComponent(TaskListComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
taskListService = TestBed.inject(TaskListService);
appConfig.config = Object.assign(appConfig.config, {
@@ -135,25 +133,16 @@ describe('TaskListComponent', () => {
it('should display loading spinner', () => {
component.isLoading = true;
const spinner = fixture.debugElement.query(By.css('.mat-progress-spinner'));
const spinner = fixture.debugElement.query(By.css('.adf-task-list-loading-margin'));
expect(spinner).toBeDefined();
});
it('should hide loading spinner upon loading complete', async () => {
component.isLoading = true;
fixture.detectChanges();
await fixture.whenStable();
let spinner = fixture.debugElement.query(By.css('.mat-progress-spinner'));
expect(spinner).toBeDefined();
expect(fixture.debugElement.query(By.css('.adf-task-list-loading-margin'))).toBeDefined();
component.isLoading = false;
fixture.detectChanges();
await fixture.whenStable();
spinner = fixture.debugElement.query(By.css('.mat-progress-spinner'));
expect(spinner).toBeNull();
expect(fixture.debugElement.query(By.css('.adf-task-list-loading-margin'))).toBeNull();
});
it('should use the default schemaColumn as default', () => {
@@ -579,23 +568,14 @@ describe('TaskListComponent', () => {
component.ngOnChanges({ sort: state });
fixture.detectChanges();
await fixture.whenStable();
const selectAllCheckbox = fixture.nativeElement.querySelector('div[class*="adf-datatable-cell-header adf-datatable-checkbox"] .mat-checkbox-inner-container');
selectAllCheckbox.click();
fixture.detectChanges();
await fixture.whenStable();
const selectAllCheckbox = await loader.getHarness(MatCheckboxHarness.with({ ancestor: '.adf-datatable-cell-header' }));
await selectAllCheckbox.toggle();
expect(component.selectedInstances.length).toBe(2);
expect(component.selectedInstances[0].obj.name).toBe('nameFake1');
expect(component.selectedInstances[1].obj.description).toBe('descriptionFake2');
selectAllCheckbox.click();
fixture.detectChanges();
await fixture.whenStable();
await selectAllCheckbox.toggle();
expect(component.selectedInstances.length).toBe(0);
});
@@ -622,16 +602,13 @@ describe('TaskListComponent', () => {
component.selectionMode = 'single';
component.ngOnChanges({ sort: state });
fixture.detectChanges();
const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container');
const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container');
selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask2.dispatchEvent(new MouseEvent('click', { bubbles: true }));
const selectTask1 = await loader.getHarness(MatCheckboxHarness.with({ ancestor: '[data-automation-id="datatable-row-0"]' }));
const selectTask2 = await loader.getHarness(MatCheckboxHarness.with({ ancestor: '[data-automation-id="datatable-row-1"]' }));
await selectTask1.toggle();
await selectTask1.toggle();
await selectTask2.toggle();
fixture.detectChanges();
await fixture.whenStable();
expect(component.selectedInstances.length).toBe(2);
});
@@ -825,6 +802,7 @@ class TaskListContextMenuComponent implements OnInit {
describe('TaskListContextMenuComponent', () => {
let fixture: ComponentFixture<TaskListContextMenuComponent>;
let customComponent: TaskListContextMenuComponent;
let loader: HarnessLoader;
let taskListService: TaskListService;
let element: HTMLElement;
@@ -840,6 +818,7 @@ describe('TaskListContextMenuComponent', () => {
});
fixture = TestBed.createComponent(TaskListContextMenuComponent);
customComponent = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
element = fixture.nativeElement;
taskListService = TestBed.inject(TaskListService);
spyOn(taskListService, 'findTasksByState').and.returnValues(of(fakeGlobalTask));
@@ -854,15 +833,12 @@ describe('TaskListContextMenuComponent', () => {
const contextMenu = element.querySelector(`[data-automation-id="text_${fakeGlobalTask.data[0].name}"]`);
const contextActionSpy = spyOn(customComponent.contextAction, 'emit').and.callThrough();
contextMenu.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }));
fixture.detectChanges();
await fixture.whenStable();
const contextActions = document.querySelectorAll('.mat-menu-item');
const contextActions = await loader.getAllHarnesses(MatMenuItemHarness);
expect(contextActions.length).toBe(2);
expect(contextActions[0]['disabled']).toBe(false, 'View Task Details action not enabled');
expect(contextActions[1]['disabled']).toBe(false, 'Cancel Task action not enabled');
contextActions[0].dispatchEvent(new Event('click'));
fixture.detectChanges();
expect(await contextActions[0].isDisabled()).toBe(false, 'View Task Details action not enabled');
expect(await contextActions[1].isDisabled()).toBe(false, 'Cancel Task action not enabled');
await contextActions[0].click();
expect(contextActionSpy).toHaveBeenCalled();
});
});