diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts index 13940564c8..c3e075500e 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts @@ -962,10 +962,8 @@ describe('FormCloudComponent', () => { expect(formComponent.isOutcomeButtonEnabled(startProcessOutcome)).toBeTruthy(); }); - it('should raise [executeOutcome] event for formService', (done) => { - formComponent.executeOutcome.subscribe(() => { - done(); - }); + it('should raise [executeOutcome] event for formService', async () => { + spyOn(formComponent.executeOutcome, 'emit'); const outcome = new FormOutcomeModel(new FormModel(), { id: FormCloudComponent.CUSTOM_OUTCOME_ID, @@ -974,6 +972,10 @@ describe('FormCloudComponent', () => { formComponent.form = new FormModel(); formComponent.onOutcomeClicked(outcome); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(formComponent.executeOutcome.emit).toHaveBeenCalledTimes(1); }); it('should refresh form values when data is changed', (done) => { diff --git a/lib/process-services-cloud/src/lib/form/components/form-definition-selector-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/form-definition-selector-cloud.component.spec.ts index a06fc1f5d6..7a4a2c2538 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-definition-selector-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-definition-selector-cloud.component.spec.ts @@ -47,31 +47,29 @@ describe('FormDefinitionCloudComponent', () => { getFormsSpy = spyOn(service, 'getStandAloneTaskForms').and.returnValue(of([{ id: 'fake-form', name: 'fakeForm' } as any])); }); - it('should load the forms by default', () => { + it('should load the forms by default', async () => { fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger'))); - clickMatSelect.triggerEventHandler('click', null); - fixture.detectChanges(); - const options: any = fixture.debugElement.queryAll(By.css('mat-option')); - expect(options[0].nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE'); - expect(options[1].nativeElement.innerText.trim()).toBe('fakeForm'); - expect(getFormsSpy).toHaveBeenCalled(); - }); + await fixture.whenStable(); + fixture.detectChanges(); + const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger'))); + clickMatSelect.triggerEventHandler('click', null); + fixture.detectChanges(); + const options: any = fixture.debugElement.queryAll(By.css('mat-option')); + expect(options[0].nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE'); + expect(options[1].nativeElement.innerText.trim()).toBe('fakeForm'); + expect(getFormsSpy).toHaveBeenCalled(); }); - it('should load only None option when no forms exist', () => { + it('should load only None option when no forms exist', async () => { getFormsSpy.and.returnValue(of([])); fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger'))); - clickMatSelect.triggerEventHandler('click', null); - fixture.detectChanges(); - const options: any = fixture.debugElement.queryAll(By.css('mat-option')); - expect((options).length).toBe(1); - }); + await fixture.whenStable(); + fixture.detectChanges(); + const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger'))); + clickMatSelect.triggerEventHandler('click', null); + fixture.detectChanges(); + const options: any = fixture.debugElement.queryAll(By.css('mat-option')); + expect((options).length).toBe(1); }); it('should not preselect any form by default', () => { @@ -81,18 +79,17 @@ describe('FormDefinitionCloudComponent', () => { expect(formInput.nodeValue).toBeNull(); }); - it('should display the name of the form that is selected', () => { + it('should display the name of the form that is selected', async () => { fixture.detectChanges(); - fixture.whenStable().then(() => { - const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger'))); - clickMatSelect.triggerEventHandler('click', null); - fixture.detectChanges(); - const options: any = fixture.debugElement.queryAll(By.css('mat-option')); - options[1].triggerEventHandler('click', {}); - fixture.detectChanges(); - const selected = fixture.debugElement.query(By.css('mat-select')); - const selectedValue = ((selected).nativeElement.innerText); - expect(selectedValue.trim()).toBe('fakeForm'); - }); + await fixture.whenStable(); + const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger'))); + clickMatSelect.triggerEventHandler('click', null); + fixture.detectChanges(); + const options: any = fixture.debugElement.queryAll(By.css('mat-option')); + options[1].triggerEventHandler('click', {}); + fixture.detectChanges(); + const selected = fixture.debugElement.query(By.css('mat-select')); + const selectedValue = ((selected).nativeElement.innerText); + expect(selectedValue.trim()).toBe('fakeForm'); }); }); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts index f8e9d20d70..93689b197a 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts @@ -57,7 +57,8 @@ import { allSourceParamsWithRelativePath, fakeLocalPhysicalRecordResponse, displayableCMParams, - fakeLocalPngHavingCMProperties + fakeLocalPngHavingCMProperties, + mockMyNodeId } from '../../../mocks/attach-file-cloud-widget.mock'; import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; @@ -182,6 +183,7 @@ describe('AttachFileCloudWidgetComponent', () => { it('should be able to attach files coming from content selector', async () => { createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], contentSourceParam); + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeId); fixture.detectChanges(); await fixture.whenStable(); clickOnAttachFileWidget('attach-file-alfresco'); @@ -218,12 +220,12 @@ describe('AttachFileCloudWidgetComponent', () => { expect(element.querySelector('#file-1155-icon')).not.toBeNull(); }); - it('should be able to set label property for Attach File widget', () => { + it('should be able to set label property for Attach File widget', async () => { createUploadWidgetField(new FormModel(), 'attach-file', [], onlyLocalParams, false, 'Label', true); fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(element.querySelector('label').innerText).toEqual('Label'); - }); + await fixture.whenStable(); + + expect(element.querySelector('label').innerText).toEqual('Label'); }); it('should reset the custom models when the component gets destroyed', () => { @@ -375,6 +377,7 @@ describe('AttachFileCloudWidgetComponent', () => { }); it('Should set default user alias (-my-) as rootNodeId if destinationFolderPath contains wrong alias and single upload for Alfresco Content + Locale', async () => { + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId); createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], allSourceWithWrongAliasParams, false); fixture.detectChanges(); await fixture.whenStable(); @@ -387,7 +390,7 @@ describe('AttachFileCloudWidgetComponent', () => { }); it('Should set default user alias (-my-) as rootNodeId if destinationFolderPath contains wrong alias and multiple upload for Alfresco Content + Locale', async () => { - + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId); createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], allSourceWithWrongAliasParams, true); fixture.detectChanges(); await fixture.whenStable(); @@ -400,6 +403,7 @@ describe('AttachFileCloudWidgetComponent', () => { }); it('Should set default user alias (-my-) as rootNodeId if destinationFolderPath does not have alias for Alfresco Content + Locale', async () => { + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId); createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], allSourceWithNoAliasParams, true); fixture.detectChanges(); await fixture.whenStable(); @@ -462,6 +466,7 @@ describe('AttachFileCloudWidgetComponent', () => { }); it('Should be able to set default user alias (-my-) as rootNodeId if the nodeId of the alias is not fetched from the api', async () => { + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId); createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], contentSourceParam, false); fixture.detectChanges(); await fixture.whenStable(); @@ -543,7 +548,11 @@ describe('AttachFileCloudWidgetComponent', () => { await fixture.whenStable(); }); - it('should remove file when remove is clicked', (done) => { + afterEach(() => { + fixture.destroy(); + }); + + it('should remove file when remove is clicked', async () => { fixture.detectChanges(); const menuButton = fixture.debugElement.query(By.css('#file-fake-properties-option-menu')).nativeElement as HTMLButtonElement; menuButton.click(); @@ -551,10 +560,8 @@ describe('AttachFileCloudWidgetComponent', () => { const removeOption = fixture.debugElement.query(By.css('#file-fake-properties-remove')).nativeElement as HTMLButtonElement; removeOption.click(); fixture.detectChanges(); - fixture.whenRenderingDone().then(() => { - expect(element.querySelector('#file-fake-properties-icon')).toBeNull(); - done(); - }); + await fixture.whenRenderingDone(); + expect(element.querySelector('#file-fake-properties-icon')).toBeNull(); }); it('should download file when download is clicked', (done) => { @@ -612,31 +619,31 @@ describe('AttachFileCloudWidgetComponent', () => { expect(updateFormSpy).toHaveBeenCalledWith(expectedValues); }); - it('should display the default menu options if no options are provided', () => { + it('should display the default menu options if no options are provided', async () => { widget.field.params = onlyLocalParams; - fixture.detectChanges(); - fixture.whenStable().then(() => { - const inputDebugElement = fixture.debugElement.query( - By.css('#attach-file-attach') - ); - inputDebugElement.triggerEventHandler('change', { - target: { files: [fakeLocalPngAnswer] } - }); - fixture.detectChanges(); - const menuButton = fixture.debugElement.query(By.css('#file-1155-option-menu')).nativeElement as HTMLButtonElement; - menuButton.click(); - fixture.detectChanges(); - - const showOption = fixture.debugElement.query(By.css('#file-1155-show-file')).nativeElement as HTMLButtonElement; - const downloadOption = fixture.debugElement.query(By.css('#file-1155-download-file')).nativeElement as HTMLButtonElement; - const retrieveMetadataOption = fixture.debugElement.query(By.css('#file-1155-retrieve-file-metadata')).nativeElement as HTMLButtonElement; - const removeOption = fixture.debugElement.query(By.css('#file-1155-remove')).nativeElement as HTMLButtonElement; - - expect(showOption).not.toBeNull(); - expect(downloadOption).not.toBeNull(); - expect(retrieveMetadataOption).toBeNull(); - expect(removeOption).not.toBeNull(); + const inputDebugElement = fixture.debugElement.query( + By.css('#attach-file-alfresco') + ); + inputDebugElement.triggerEventHandler('change', { + target: { files: [fakeLocalPngAnswer] } }); + fixture.detectChanges(); + await fixture.whenStable(); + + const menuButton = fixture.debugElement.query(By.css('#file-fake-properties-option-menu')).nativeElement as HTMLButtonElement; + menuButton.click(); + fixture.detectChanges(); + await fixture.whenStable(); + + const showOption = fixture.debugElement.query(By.css('#file-fake-properties-show-file')); + const downloadOption = fixture.debugElement.query(By.css('#file-fake-properties-download-file')); + const retrieveMetadataOption = fixture.debugElement.query(By.css('#file-fake-properties-retrieve-file-metadata')); + const removeOption = fixture.debugElement.query(By.css('#file-fake-properties-remove')); + + expect(showOption).not.toBeNull(); + expect(downloadOption).not.toBeNull(); + expect(retrieveMetadataOption).toBeNull(); + expect(removeOption).not.toBeNull(); }); }); @@ -711,6 +718,7 @@ describe('AttachFileCloudWidgetComponent', () => { }); it('should have been called on attach file when value was empty', async () => { + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeId); clickOnAttachFileWidget('attach-file-alfresco'); fixture.detectChanges(); await fixture.whenStable(); @@ -721,6 +729,7 @@ describe('AttachFileCloudWidgetComponent', () => { }); it('should not be called on attach file when has a file previously', async () => { + spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeId); widget.field.value = [fakeMinimalNode]; clickOnAttachFileWidget('attach-file-alfresco'); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.spec.ts index 0f2d7b9460..88d7cd983b 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.spec.ts @@ -19,6 +19,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { FileViewerWidgetComponent } from './file-viewer.widget'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormModel, FormService, FormFieldModel } from '@alfresco/adf-core'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; describe('FileViewerWidgetComponent', () => { const fakeForm = new FormModel(); @@ -47,7 +48,8 @@ describe('FileViewerWidgetComponent', () => { TranslateModule.forRoot() ], declarations: [ FileViewerWidgetComponent ], - providers: [ { provide: FormService, useValue: formServiceStub } ] + providers: [ { provide: FormService, useValue: formServiceStub } ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }); formServiceStub = TestBed.inject(FormService); diff --git a/lib/process-services-cloud/src/lib/form/mocks/attach-file-cloud-widget.mock.ts b/lib/process-services-cloud/src/lib/form/mocks/attach-file-cloud-widget.mock.ts index 75df6bb736..f4f7294599 100644 --- a/lib/process-services-cloud/src/lib/form/mocks/attach-file-cloud-widget.mock.ts +++ b/lib/process-services-cloud/src/lib/form/mocks/attach-file-cloud-widget.mock.ts @@ -292,6 +292,8 @@ export const mockNodeId = new Promise((resolve) => { resolve('mock-node-id'); }); +export const mockMyNodeId = Promise.resolve('-my-'); + export const mockNodeIdBasedOnStringVariableValue = new Promise((resolve) => { resolve('mock-string-value-node-id'); }); diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts index ff50628676..ceaa62362d 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts @@ -443,16 +443,6 @@ describe('EditProcessFilterCloudComponent', () => { }); }); - it('should have floating labels when values are present', async () => { - fixture.detectChanges(); - await fixture.whenStable(); - - const inputLabelsNodes = document.querySelectorAll('mat-form-field'); - inputLabelsNodes.forEach(labelNode => { - expect(labelNode.getAttribute('ng-reflect-float-label')).toBe('auto'); - }); - }); - it('should be able to filter filterProperties when input is defined', async () => { fixture.detectChanges(); await fixture.whenStable(); diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts index 2fd1e24389..1caf39d74d 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts @@ -16,13 +16,15 @@ */ import { Component, SimpleChange, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { AppConfigService, ColumnsSelectorComponent, + CustomEmptyContentTemplateDirective, DataColumn, DataRowEvent, + DataTableModule, getDataColumnMock, ObjectDataRow, setupTestBed @@ -39,6 +41,9 @@ import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service'; import { ProcessListCloudPreferences } from '../models/process-cloud-preferences'; import { PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data'; +import { HttpClientModule } from '@angular/common/http'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @Component({ template: ` @@ -59,19 +64,6 @@ class CustomTaskListComponent { processListCloud: ProcessListCloudComponent; } -@Component({ - template: ` - - -

TEST

-
-
- ` -}) - -class EmptyTemplateComponent { -} - describe('ProcessListCloudComponent', () => { let component: ProcessListCloudComponent; let fixture: ComponentFixture; @@ -558,14 +550,33 @@ describe('ProcessListCloudComponent', () => { describe('Creating an empty custom template - EmptyTemplateComponent', () => { + @Component({ + template: ` + + +

TEST

+
+
+ ` + }) + + class EmptyTemplateComponent { + @ViewChild(ProcessListCloudComponent) + processListCloud: ProcessListCloudComponent; + } + let fixtureEmpty: ComponentFixture; setupTestBed({ imports: [ TranslateModule.forRoot(), - ProcessServiceCloudTestingModule + HttpClientModule, + NoopAnimationsModule, + DataTableModule, + MatProgressSpinnerModule ], - declarations: [EmptyTemplateComponent] + providers: [{ provide: PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, useExisting: LocalPreferenceCloudService }], + declarations: [EmptyTemplateComponent, ProcessListCloudComponent, CustomEmptyContentTemplateDirective] }); beforeEach(() => { @@ -577,14 +588,16 @@ describe('ProcessListCloudComponent', () => { fixtureEmpty.destroy(); }); - it('should render the custom template', fakeAsync((done) => { + it('should render the custom template', async () => { const emptyList = {list: {entries: []}}; spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList)); - component.success.subscribe(() => { - expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull(); - expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); - done(); - }); - })); + fixtureEmpty.componentInstance.processListCloud.isLoading = false; + fixtureEmpty.detectChanges(); + await fixtureEmpty.whenStable(); + + expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull(); + expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); + + }); }); }); diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts index 6c7b72232b..96579dde02 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts @@ -61,13 +61,14 @@ describe('StartProcessCloudComponent', () => { const firstChange = new SimpleChange(undefined, 'myApp', true); - 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); + await fixture.whenStable(); + const options: any = fixture.debugElement.queryAll(By.css('.mat-autocomplete-panel .mat-option')); + const currentOption: DebugElement = options.find((option: DebugElement) => option.nativeElement.querySelector('.mat-option-text').innerHTML.trim() === name); if (currentOption) { currentOption.nativeElement.click(); @@ -104,7 +105,8 @@ describe('StartProcessCloudComponent', () => { fixture = TestBed.createComponent(StartProcessCloudComponent); component = fixture.componentInstance; - getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(of(fakeProcessDefinitions)); + getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions'); + formDefinitionSpy = spyOn(formCloudService, 'getForm'); spyOn(processService, 'updateProcess').and.returnValue(of()); startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(of(fakeProcessInstance)); getStartEventFormStaticValuesMappingSpy = spyOn(processService, 'getStartEventFormStaticValuesMapping').and.returnValue(of([])); @@ -118,8 +120,9 @@ describe('StartProcessCloudComponent', () => { describe('start a process without start form', () => { beforeEach(() => { - component.name = 'My new process'; + component.name = 'My formless new process'; component.appName = 'myApp'; + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); fixture.detectChanges(); const change = new SimpleChange(null, 'MyApp', true); component.ngOnChanges({ appName: change }); @@ -137,28 +140,25 @@ describe('StartProcessCloudComponent', () => { fixture.detectChanges(); tick(550); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.isProcessFormValid()).toBe(true); - expect(startBtn.disabled).toBe(false); - }); + fixture.detectChanges(); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(component.isProcessFormValid()).toBe(true); + expect(startBtn.disabled).toBe(false); })); - it('should create a process instance if the selection is valid', fakeAsync(() => { + it('should create a process instance if the selection is valid', async () => { getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.name = 'My new process'; component.processDefinitionName = 'process'; - selectOptionByName('process'); - - fixture.whenStable().then(() => { - expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[1])).name); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.isProcessFormValid()).toBe(true); - expect(startBtn.disabled).toBe(false); - }); - })); + await selectOptionByName('processwithoutform2'); + fixture.detectChanges(); + await fixture.whenStable(); + expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[1])).name); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(component.isProcessFormValid()).toBe(true); + expect(startBtn.disabled).toBe(false); + }); it('should have start button disabled when no process is selected', async () => { component.name = ''; @@ -206,10 +206,7 @@ describe('StartProcessCloudComponent', () => { component.ngOnChanges({ appName: change }); fixture.detectChanges(); tick(550); - - fixture.whenStable().then(() => { - expect(component.resolvedValues).toEqual(staticInputs.concat(values)); - }); + expect(component.resolvedValues).toEqual(staticInputs.concat(values)); })); }); @@ -217,6 +214,11 @@ describe('StartProcessCloudComponent', () => { beforeEach(() => { component.name = 'My new process with form'; + component.appName = 'startformwithoutupload'; + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + fixture.detectChanges(); + const change = new SimpleChange(null, 'startformwithoutupload', true); + component.ngOnChanges({ appName: change }); component.values = [{ id: '1', type: 'string', @@ -239,162 +241,113 @@ describe('StartProcessCloudComponent', () => { this['value'] = value; } }]; + fixture.detectChanges(); }); - it('should be able to start a process with a valid form', fakeAsync(() => { - component.processDefinitionName = 'processwithform'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName))); - fixture.detectChanges(); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm)); - - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - tick(); + it('should be able to start a process with a valid form', async () => { + formDefinitionSpy.and.returnValue(of(fakeStartForm)); + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform'))); typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); - tick(550); + await fixture.whenStable(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const firstNameEl = fixture.nativeElement.querySelector('#firstName'); - expect(firstNameEl).toBeDefined(); - const lastNameEl = fixture.nativeElement.querySelector('#lastName'); - expect(lastNameEl).toBeDefined(); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.formCloud.isValid).toBe(true); - expect(startBtn.disabled).toBe(false); - }); - })); - - it('should NOT be able to start a process with a form NOT valid', fakeAsync(() => { - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName))); fixture.detectChanges(); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartFormNotValid)); + const firstNameEl = fixture.nativeElement.querySelector('#firstName'); + expect(firstNameEl).toBeDefined(); + const lastNameEl = fixture.nativeElement.querySelector('#lastName'); + expect(lastNameEl).toBeDefined(); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(component.formCloud.isValid).toBe(true); + expect(startBtn.disabled).toBe(false); + }); - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - - tick(); + it('should NOT be able to start a process with a form NOT valid', async () => { + formDefinitionSpy.and.returnValue(of(fakeStartFormNotValid)); + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform'))); typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); - tick(550); + await fixture.whenStable(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const firstNameEl = fixture.nativeElement.querySelector('#firstName'); - expect(firstNameEl).toBeDefined(); - const lastNameEl = fixture.nativeElement.querySelector('#lastName'); - expect(lastNameEl).toBeDefined(); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.formCloud.isValid).toBe(false); - expect(startBtn.disabled).toBe(true); - }); - })); - - it('should be able to start a process with a prefilled valid form', fakeAsync(() => { - component.processDefinitionName = 'processwithform'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName))); fixture.detectChanges(); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm)); + const firstNameEl = fixture.nativeElement.querySelector('#firstName'); + expect(firstNameEl).toBeDefined(); + const lastNameEl = fixture.nativeElement.querySelector('#lastName'); + expect(lastNameEl).toBeDefined(); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(component.formCloud.isValid).toBe(false); + expect(startBtn.disabled).toBe(true); + }); - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - - tick(); + it('should be able to start a process with a prefilled valid form', async () => { + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform'))); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); - tick(550); + await fixture.whenStable(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const firstNameEl = fixture.nativeElement.querySelector('#firstName'); - expect(firstNameEl).toBeDefined(); - expect(firstNameEl.value).toEqual('FakeName'); - const lastNameEl = fixture.nativeElement.querySelector('#lastName'); - expect(lastNameEl).toBeDefined(); - expect(lastNameEl.value).toEqual('FakeLastName'); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.formCloud.isValid).toBe(true); - expect(startBtn.disabled).toBe(false); - }); - })); - - it('should NOT be able to start a process with a prefilled NOT valid form', fakeAsync(() => { - component.processDefinitionName = 'processwithform'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName))); fixture.detectChanges(); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartFormNotValid)); + await fixture.whenStable(); + const firstNameEl = fixture.nativeElement.querySelector('#firstName'); + expect(firstNameEl).toBeDefined(); + expect(firstNameEl.value).toEqual('FakeName'); + const lastNameEl = fixture.nativeElement.querySelector('#lastName'); + expect(lastNameEl).toBeDefined(); + expect(lastNameEl.value).toEqual('FakeLastName'); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(component.formCloud.isValid).toBe(true); + expect(startBtn.disabled).toBe(false); + }); - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - - tick(); + it('should NOT be able to start a process with a prefilled NOT valid form', async () => { + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform'))); + formDefinitionSpy.and.returnValue(of(fakeStartFormNotValid)); typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); typeValueInto('#processDefinitionName', 'processwithform'); fixture.detectChanges(); - tick(4500); + await fixture.whenStable(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(formDefinitionSpy).toHaveBeenCalled(); - const firstNameEl = fixture.nativeElement.querySelector('#firstName'); - expect(firstNameEl).toBeDefined(); - expect(firstNameEl.value).toEqual('FakeName'); - const lastNameEl = fixture.nativeElement.querySelector('#lastName'); - expect(lastNameEl).toBeDefined(); - expect(lastNameEl.value).toEqual('FakeLastName'); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.formCloud.isValid).toBe(false); - expect(startBtn.disabled).toBe(true); - }); - })); - - it('should display enabled start process button if the selection is valid', fakeAsync(() => { - component.name = 'testFormWithProcess'; - component.processDefinitionName = 'processwithoutform2'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName))); fixture.detectChanges(); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm)); + await fixture.whenStable(); + expect(formDefinitionSpy).toHaveBeenCalled(); + const firstNameEl = fixture.nativeElement.querySelector('#firstName'); + expect(firstNameEl).toBeDefined(); + expect(firstNameEl.value).toEqual('FakeName'); + const lastNameEl = fixture.nativeElement.querySelector('#lastName'); + expect(lastNameEl).toBeDefined(); + expect(lastNameEl.value).toEqual('FakeLastName'); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(component.formCloud.isValid).toBe(false); + expect(startBtn.disabled).toBe(true); + }); + + it('should display enabled start process button if the selection is valid', async () => { + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2'))); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); + typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form'); + typeValueInto('#processDefinitionName', 'processwithoutform2'); + fixture.detectChanges(); + await fixture.whenStable(); + + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(startBtn.disabled).toBe(false); + expect(component.isProcessFormValid()).toBe(true); + }); + + it('should have start button enabled when default values are set', async () => { + getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2'))); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); const change = new SimpleChange(null, 'MyApp', true); component.ngOnChanges({ appName: change }); fixture.detectChanges(); - tick(550); + await fixture.whenStable(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(startBtn.disabled).toBe(false); - expect(component.formCloud.isValid).toBe(true); - expect(component.isProcessFormValid()).toBe(true); - }); - })); - - it('should have start button enabled when default values are set', fakeAsync(() => { - component.name = 'testFormWithProcess'; - component.processDefinitionName = 'processwithoutform2'; - getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName))); - fixture.detectChanges(); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm)); - - const change = new SimpleChange(null, 'MyApp', true); - component.ngOnChanges({ appName: change }); - fixture.detectChanges(); - tick(550); - - fixture.whenStable().then(() => { - fixture.detectChanges(); - const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(startBtn.disabled).toBe(false); - }); - })); + const startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(startBtn.disabled).toBe(false); + }); }); describe('process definitions list', () => { @@ -402,55 +355,60 @@ describe('StartProcessCloudComponent', () => { beforeEach(() => { component.name = 'My new process'; component.appName = 'myApp'; + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); fixture.detectChanges(); const change = new SimpleChange(null, 'MyApp', true); component.ngOnChanges({ appName: change }); fixture.detectChanges(); }); - it('should call service to fetch process definitions with appId', () => { - fixture.whenStable().then(() => { - expect(getDefinitionsSpy).toHaveBeenCalledWith('myApp'); - }); + it('should call service to fetch process definitions with appId', async () => { + await fixture.whenStable(); + + expect(getDefinitionsSpy).toHaveBeenCalledWith('MyApp'); }); - it('should display the correct number of processes in the select list', () => { - fixture.whenStable().then(() => { - const selectElement = fixture.nativeElement.querySelector('mat-select'); - expect(selectElement.children.length).toBe(1); - }); - }); + it('should display the correct number of processes in the select list', async () => { + await fixture.whenStable(); - it('should display the option def details', () => { - component.processDefinitionList = fakeProcessDefinitions; + const arrowButton = fixture.nativeElement.querySelector('button#adf-select-process-dropdown'); + arrowButton.click(); fixture.detectChanges(); - fixture.whenStable().then(() => { - const selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); - const optionElement = fixture.nativeElement.querySelectorAll('mat-option'); - selectElement.click(); - expect(selectElement).not.toBeNull(); - expect(selectElement).toBeDefined(); - expect(optionElement).not.toBeNull(); - expect(optionElement).toBeDefined(); - }); + const processLists = fixture.debugElement.query(By.css('.mat-autocomplete-panel')); + expect(processLists.children.length).toBe(4); }); - it('should display the key when the processDefinition name is empty or null', () => { + it('should display the option def details', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + + const selectElement = fixture.nativeElement.querySelector('button#adf-select-process-dropdown'); + selectElement.click(); + fixture.detectChanges(); + const optionElement = fixture.debugElement.queryAll(By.css('.mat-autocomplete-panel .mat-option')); + expect(selectElement).not.toBeNull(); + expect(selectElement).toBeDefined(); + expect(optionElement).not.toBeNull(); + expect(optionElement).toBeDefined(); + }); + + it('should display the key when the processDefinition name is empty or null', async () => { component.processDefinitionList = fakeNoNameProcessDefinitions; fixture.detectChanges(); - fixture.whenStable().then(() => { - const selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); - const optionElement = fixture.nativeElement.querySelectorAll('mat-option'); - selectElement.click(); - expect(selectElement).not.toBeNull(); - expect(selectElement).toBeDefined(); - expect(optionElement).not.toBeNull(); - expect(optionElement[0].textContent).toBe('NewProcess 1'); - }); + await fixture.whenStable(); + + const selectElement = fixture.nativeElement.querySelector('button#adf-select-process-dropdown'); + selectElement.click(); + fixture.detectChanges(); + const optionElement = fixture.debugElement.queryAll(By.css('.mat-autocomplete-panel .mat-option')); + expect(selectElement).not.toBeNull(); + expect(selectElement).toBeDefined(); + expect(optionElement).not.toBeNull(); + expect(optionElement[0].nativeElement.textContent.trim()).toBe('NewProcess 1'); }); it('should indicate an error to the user if process defs cannot be loaded', async () => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(throwError({})); + getDefinitionsSpy.and.returnValue(throwError({})); const change = new SimpleChange('myApp', 'myApp1', true); component.ngOnChanges({ appName: change }); @@ -462,7 +420,7 @@ describe('StartProcessCloudComponent', () => { }); it('should show no process available message when no process definition is loaded', async () => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([])); + getDefinitionsSpy.and.returnValue(of([])); const change = new SimpleChange('myApp', 'myApp1', true); component.ngOnChanges({ appName: change }); @@ -475,7 +433,7 @@ describe('StartProcessCloudComponent', () => { }); it('should select automatically the processDefinition if the app contain only one', async () => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]])); + getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]])); const change = new SimpleChange('myApp', 'myApp1', true); component.ngOnChanges({ appName: change }); @@ -485,29 +443,30 @@ describe('StartProcessCloudComponent', () => { expect(component.processForm.controls['processDefinition'].value).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[0])).name); }); - it('should select automatically the form when processDefinition is selected as default', fakeAsync(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]])); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm)); + it('should select automatically the form when processDefinition is selected as default', async () => { + getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]])); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); const change = new SimpleChange('myApp', 'myApp1', true); - component.ngOnInit(); component.ngOnChanges({ appName: change }); - component.processForm.controls['processDefinition'].setValue('process'); fixture.detectChanges(); - tick(3000); - component.processDefinitionName = fakeProcessDefinitions[0].name; - component.setProcessDefinitionOnForm(fakeProcessDefinitions[0].name); - fixture.detectChanges(); - tick(3000); + await fixture.whenStable(); - fixture.whenStable().then(() => { - const processForm = fixture.nativeElement.querySelector('adf-cloud-form'); - expect(component.hasForm()).toBeTruthy(); - expect(processForm).not.toBeNull(); - }); - })); + await selectOptionByName('processwithform'); + fixture.detectChanges(); + await fixture.whenStable(); + + component.processDefinitionName = fakeProcessDefinitions[2].name; + component.setProcessDefinitionOnForm(fakeProcessDefinitions[2].name); + fixture.detectChanges(); + await fixture.whenStable(); + + const processForm = fixture.nativeElement.querySelector('adf-cloud-form'); + expect(component.hasForm()).toBeTruthy(); + expect(processForm).not.toBeNull(); + }); it('should not select automatically any processDefinition if the app contain multiple process and does not have any processDefinition as input', async () => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.appName = 'myApp'; component.ngOnChanges({}); @@ -517,23 +476,27 @@ describe('StartProcessCloudComponent', () => { expect(component.processPayloadCloud.name).toBeNull(); }); - it('should select the right process when the processKey begins with the name', fakeAsync(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + it('should select the right process when the processKey begins with the name', async () => { + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); component.name = 'My new process'; component.processDefinitionName = 'process'; + component.appName = 'myApp'; + component.ngOnChanges({}); selectOptionByName('process'); - fixture.whenStable().then(() => { - expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).name); - expect(component.processDefinitionCurrent.key).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).key); - }); - })); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).name); + expect(component.processDefinitionCurrent.key).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).key); + }); describe('dropdown', () => { it('should hide the process dropdown button if showSelectProcessDropdown is false', async () => { fixture.detectChanges(); - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.appName = 'myApp'; component.showSelectProcessDropdown = false; component.ngOnChanges({}); @@ -547,7 +510,7 @@ describe('StartProcessCloudComponent', () => { it('should show the process dropdown button if showSelectProcessDropdown is false', async () => { fixture.detectChanges(); - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.appName = 'myApp'; component.processDefinitionName = 'NewProcess 2'; component.showSelectProcessDropdown = true; @@ -562,7 +525,7 @@ describe('StartProcessCloudComponent', () => { it('should show the process dropdown button by default', async () => { fixture.detectChanges(); - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.appName = 'myApp'; component.processDefinitionName = 'NewProcess 2'; component.ngOnChanges({}); @@ -581,25 +544,27 @@ describe('StartProcessCloudComponent', () => { const change = new SimpleChange('myApp', 'myApp1', false); beforeEach(() => { + component.name = 'My new process'; component.appName = 'myApp'; fixture.detectChanges(); }); it('should have floating labels for process name and type', async () => { - component.appName = 'myApp'; - component.processDefinitionName = 'NewProcess 2'; - component.ngOnChanges({}); - + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + component.ngOnChanges({ appName: change }); fixture.detectChanges(); await fixture.whenStable(); + component.processForm.controls.processInstanceName.setValue('My sharona'); + component.processForm.controls.processDefinition.setValue('process'); - const inputLabelsNodes = document.querySelectorAll('.adf-start-process .adf-process-input-container'); - inputLabelsNodes.forEach(labelNode => { - expect(labelNode.getAttribute('ng-reflect-float-label')).toBe('always'); - }); + fixture.detectChanges(); + + const inputLabelsNodes = document.querySelectorAll('.mat-form-field-label'); + expect(inputLabelsNodes.length).toBe(2); }); it('should reload processes when appName input changed', async () => { + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.ngOnChanges({ appName: firstChange }); component.ngOnChanges({ appName: change }); @@ -610,6 +575,7 @@ describe('StartProcessCloudComponent', () => { }); it('should reload processes ONLY when appName input changed', async () => { + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.ngOnChanges({ appName: firstChange }); fixture.detectChanges(); @@ -621,28 +587,30 @@ describe('StartProcessCloudComponent', () => { }); it('should get current processDef', () => { + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.ngOnChanges({ appName: change }); fixture.detectChanges(); expect(getDefinitionsSpy).toHaveBeenCalled(); expect(component.processDefinitionList).toBe(fakeProcessDefinitions); }); - it('should display the matching results in the dropdown as the user types down', fakeAsync(() => { - component.processDefinitionList = fakeProcessDefinitions; - component.ngOnInit(); + it('should display the matching results in the dropdown as the user types down', async () => { + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.ngOnChanges({ appName: change }); fixture.detectChanges(); - component.processForm.controls['processDefinition'].setValue('process'); + typeValueInto('#processDefinitionName', 'process'); fixture.detectChanges(); - tick(3000); + await fixture.whenStable(); + expect(component.filteredProcesses.length).toEqual(4); - component.processForm.controls['processDefinition'].setValue('processwithfo'); + typeValueInto('#processDefinitionName', 'processwithfo'); fixture.detectChanges(); - tick(3000); + await fixture.whenStable(); + expect(component.filteredProcesses.length).toEqual(1); - })); + }); it('should display the process definion field as empty if are more than one process definition in the list', async () => { getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); @@ -662,7 +630,8 @@ describe('StartProcessCloudComponent', () => { fixture.detectChanges(); component.name = 'NewProcess 1'; component.appName = 'myApp'; - component.ngOnChanges({}); + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + fixture.detectChanges(); }); it('should see start button', async () => { @@ -746,19 +715,22 @@ describe('StartProcessCloudComponent', () => { expect(startProcessSpy).toHaveBeenCalledWith(component.appName, payload); }); - it('should call service with the correct parameters when variables are undefined and formCloud is defined', fakeAsync(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]])); - formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm)); + it('should call service with the correct parameters when variables are undefined and formCloud is defined', async () => { + getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]])); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); const change = new SimpleChange('myApp', 'myApp1', true); - component.ngOnInit(); component.ngOnChanges({ appName: change }); - component.processForm.controls['processDefinition'].setValue('process'); fixture.detectChanges(); - tick(3000); - component.processDefinitionName = fakeProcessDefinitions[0].name; - component.setProcessDefinitionOnForm(fakeProcessDefinitions[0].name); + await fixture.whenStable(); + + await selectOptionByName('processwithform'); fixture.detectChanges(); - tick(3000); + await fixture.whenStable(); + + component.processDefinitionName = fakeProcessDefinitions[2].name; + component.setProcessDefinitionOnForm(fakeProcessDefinitions[2].name); + fixture.detectChanges(); + await fixture.whenStable(); const processForm = fixture.nativeElement.querySelector('adf-cloud-form'); expect(component.hasForm()).toBeTruthy(); @@ -766,7 +738,7 @@ describe('StartProcessCloudComponent', () => { const payload: ProcessPayloadCloud = new ProcessPayloadCloud({ name: component.processInstanceName.value, - processDefinitionKey: fakeProcessDefinitions[0].key, + processDefinitionKey: fakeProcessDefinitions[2].key, variables: Object.assign({}, component.formCloud.values) }); const startButton = fixture.debugElement.query(By.css('#button-start')); @@ -774,7 +746,7 @@ describe('StartProcessCloudComponent', () => { startButton.triggerEventHandler('click', null); expect(startProcessSpy).toHaveBeenCalledWith(component.appName, payload); - })); + }); it('should output start event when process started successfully', () => { const emitSpy = spyOn(component.success, 'emit'); @@ -858,16 +830,18 @@ describe('StartProcessCloudComponent', () => { expect(startBtn.disabled).toBe(true); }); - it('should emit processDefinitionSelection event when a process definition is selected', (done) => { + it('should emit processDefinitionSelection event when a process definition is selected', async () => { + const emitSpy = spyOn(component.processDefinitionSelection, 'emit'); component.appName = 'myApp'; + formDefinitionSpy.and.returnValue(of(fakeStartForm)); component.ngOnChanges({ appName: firstChange }); - - component.processDefinitionSelection.subscribe((processDefinition) => { - expect(processDefinition).toEqual(fakeProcessDefinitions[0]); - done(); - }); fixture.detectChanges(); + await fixture.whenStable(); + component.processDefinitionName = 'processwithoutform1'; selectOptionByName(fakeProcessDefinitions[0].name); + fixture.detectChanges(); + await fixture.whenStable(); + expect(emitSpy).toHaveBeenCalledOnceWith(fakeProcessDefinitions[0]); }); it('should wait for process definition to be loaded before showing the empty process definition message', () => { @@ -883,17 +857,18 @@ describe('StartProcessCloudComponent', () => { expect(noProcessElement).not.toBeNull(); }); - it('should set the process name using the processName cloud pipe when a process definition gets selected', () => { + it('should set the process name using the processName cloud pipe when a process definition gets selected', async () => { const processNameCloudPipe = TestBed.inject(ProcessNameCloudPipe); const processNamePipeTransformSpy = spyOn(processNameCloudPipe, 'transform').and.returnValue('fake-transformed-name'); const expectedProcessInstanceDetails: ProcessInstanceCloud = { processDefinitionName: fakeProcessDefinitions[0].name }; - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); + getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]])); + formDefinitionSpy.and.stub(); component.appName = 'myApp'; component.ngOnChanges({ appName: firstChange }); - fixture.detectChanges(); - selectOptionByName(fakeProcessDefinitions[0].name); + fixture.detectChanges(); + await fixture.whenStable(); expect(processNamePipeTransformSpy).toHaveBeenCalledWith(component.name, expectedProcessInstanceDetails); expect(component.processInstanceName.dirty).toBe(true); @@ -1002,6 +977,7 @@ describe('StartProcessCloudComponent', () => { beforeEach(() => { component.name = 'NewProcess 1'; component.appName = 'myApp'; + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.ngOnChanges({ appName: firstChange }); component.processDefinitionList = fakeProcessDefinitions; component.processDefinitionName = fakeProcessDefinitions[0].name; @@ -1040,6 +1016,7 @@ describe('StartProcessCloudComponent', () => { beforeEach(() => { component.name = 'NewProcess 1'; component.appName = 'myApp'; + getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.ngOnChanges({ appName: firstChange }); fixture.detectChanges(); }); diff --git a/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts index 80546ea7c0..9ed21758a5 100644 --- a/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts @@ -80,7 +80,7 @@ describe('PreferenceService', () => { }); }); - it('Should not fetch preferences if error occurred', () => { + it('Should not fetch preferences if error occurred', (done) => { getInstanceSpy.and.returnValue(apiErrorMock); service.getPreferences('mock-app-name') .subscribe( @@ -89,6 +89,7 @@ describe('PreferenceService', () => { expect(error.state).toEqual(404); expect(error.stateText).toEqual('Not Found'); expect(error.error).toEqual('Mock Error'); + done(); } ); }); @@ -107,7 +108,7 @@ describe('PreferenceService', () => { }); }); - it('Should not fetch preference by key if error occurred', () => { + it('Should not fetch preference by key if error occurred', (done) => { getInstanceSpy.and.returnValue(apiErrorMock); service.getPreferenceByKey('mock-app-name', 'mock-preference-key') .subscribe( @@ -116,6 +117,7 @@ describe('PreferenceService', () => { expect(error.state).toEqual(404); expect(error.stateText).toEqual('Not Found'); expect(error.error).toEqual('Mock Error'); + done(); } ); }); @@ -132,7 +134,7 @@ describe('PreferenceService', () => { }); }); - it('Should not create preference if error occurred', () => { + it('Should not create preference if error occurred', (done) => { getInstanceSpy.and.returnValue(apiErrorMock); service.createPreference('mock-app-name', 'mock-preference-key', createMockPreference) .subscribe( @@ -141,6 +143,7 @@ describe('PreferenceService', () => { expect(error.state).toEqual(404); expect(error.stateText).toEqual('Not Found'); expect(error.error).toEqual('Mock Error'); + done(); } ); }); @@ -157,7 +160,7 @@ describe('PreferenceService', () => { }); }); - it('Should not update preference if error occurred', () => { + it('Should not update preference if error occurred', (done) => { getInstanceSpy.and.returnValue(apiErrorMock); service.createPreference('mock-app-name', 'mock-preference-key', updateMockPreference) .subscribe( @@ -166,6 +169,7 @@ describe('PreferenceService', () => { expect(error.state).toEqual(404); expect(error.stateText).toEqual('Not Found'); expect(error.error).toEqual('Mock Error'); + done(); } ); }); @@ -178,7 +182,7 @@ describe('PreferenceService', () => { }); }); - it('Should not delete preference if error occurred', () => { + it('Should not delete preference if error occurred', (done) => { getInstanceSpy.and.returnValue(apiErrorMock); service.deletePreference('mock-app-name', 'mock-preference-key') .subscribe( @@ -187,6 +191,7 @@ describe('PreferenceService', () => { expect(error.state).toEqual(404); expect(error.stateText).toEqual('Not Found'); expect(error.error).toEqual('Mock Error'); + done(); } ); }); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts index 5f27f37741..1ed8964ca0 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts @@ -15,12 +15,12 @@ * limitations under the License. */ -import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SimpleChange } from '@angular/core'; import { By } from '@angular/platform-browser'; import { setupTestBed } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material/dialog'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service'; import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service'; @@ -35,6 +35,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { EditServiceTaskFilterCloudComponent } from './edit-service-task-filter-cloud.component'; import { MatIconTestingModule } from '@angular/material/icon/testing'; import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model'; +import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component'; describe('EditServiceTaskFilterCloudComponent', () => { let component: EditServiceTaskFilterCloudComponent; @@ -45,6 +46,7 @@ describe('EditServiceTaskFilterCloudComponent', () => { let getTaskFilterSpy: jasmine.Spy; let getRunningApplicationsSpy: jasmine.Spy; let taskService: TaskCloudService; + const afterClosedSubject = new Subject(); setupTestBed({ imports: [ @@ -66,13 +68,10 @@ describe('EditServiceTaskFilterCloudComponent', () => { appsService = TestBed.inject(AppsProcessCloudService); taskService = TestBed.inject(TaskCloudService); dialog = TestBed.inject(MatDialog); - spyOn(dialog, 'open').and.returnValue({ - afterClosed: of({ - action: EditServiceTaskFilterCloudComponent.ACTION_SAVE, - icon: 'icon', - name: 'fake-name' - }) - } as any); + const dialogRefMock: any = { + afterClosed: () => afterClosedSubject + }; + spyOn(dialog, 'open').and.returnValue(dialogRefMock); getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeServiceFilter)); getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance)); fixture.detectChanges(); @@ -80,18 +79,17 @@ describe('EditServiceTaskFilterCloudComponent', () => { afterEach(() => fixture.destroy()); - it('should fetch task filter by taskId', () => { + it('should fetch task filter by taskId', async () => { const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true); component.ngOnChanges({ id: taskFilterIdChange }); fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(getTaskFilterSpy).toHaveBeenCalled(); - expect(component.taskFilter.name).toEqual('FakeInvolvedTasks'); - expect(component.taskFilter.icon).toEqual('adjust'); - expect(component.taskFilter.status).toEqual('CREATED'); - expect(component.taskFilter.order).toEqual('ASC'); - expect(component.taskFilter.sort).toEqual('id'); - }); + await fixture.whenStable(); + expect(getTaskFilterSpy).toHaveBeenCalled(); + expect(component.taskFilter.name).toEqual('FakeInvolvedTasks'); + expect(component.taskFilter.icon).toEqual('adjust'); + expect(component.taskFilter.status).toEqual('COMPLETED'); + expect(component.taskFilter.order).toEqual('ASC'); + expect(component.taskFilter.sort).toEqual('id'); }); it('should fetch process definitions when processDefinitionName filter property is set', async () => { @@ -636,7 +634,7 @@ describe('EditServiceTaskFilterCloudComponent', () => { spyOn(component.action, 'emit').and.callThrough(); }); - it('should emit save event and save the filter on click save button', fakeAsync(() => { + it('should emit save event and save the filter on click save button', async () => { component.toggleFilterActions = true; spyOn(service, 'updateFilter').and.returnValue(of(null)); fixture.detectChanges(); @@ -650,16 +648,15 @@ describe('EditServiceTaskFilterCloudComponent', () => { sortOptions[3].nativeElement.click(); fixture.detectChanges(); const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(saveButton.disabled).toBe(false); - saveButton.click(); - expect(service.updateFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - }); - })); + await fixture.whenStable(); + fixture.detectChanges(); + expect(saveButton.disabled).toBe(false); + saveButton.click(); + expect(service.updateFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); + }); - it('should emit delete event and delete the filter on click of delete button', fakeAsync(() => { + it('should emit delete event and delete the filter on click of delete button', async () => { component.toggleFilterActions = true; spyOn(service, 'deleteFilter').and.returnValue(of(null)); fixture.detectChanges(); @@ -670,40 +667,48 @@ describe('EditServiceTaskFilterCloudComponent', () => { stateElement.click(); fixture.detectChanges(); const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(deleteButton.disabled).toBe(false); - deleteButton.click(); - expect(service.deleteFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - }); - })); + await fixture.whenStable(); + fixture.detectChanges(); + expect(deleteButton.disabled).toBe(false); + deleteButton.click(); + expect(service.deleteFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); + }); - it('should emit saveAs event and add filter on click saveAs button', fakeAsync(() => { + it('should emit saveAs event and add filter on click saveAs button', async () => { component.toggleFilterActions = true; spyOn(service, 'addFilter').and.returnValue(of(null)); fixture.detectChanges(); + const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); - sortElement.click(); - fixture.detectChanges(); - const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); - sortOptions[2].nativeElement.click(); - fixture.detectChanges(); - const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(saveAsButton.disabled).toBe(false); - saveAsButton.click(); - expect(service.addFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - expect(dialog.open).toHaveBeenCalled(); - }); - })); - it('should call restore default filters service on deletion of last filter', fakeAsync(() => { + const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + stateElement.click(); + fixture.detectChanges(); + + const sortOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-sort"] .mat-option-ripple')); + sortOptions[3].nativeElement.click(); + fixture.detectChanges(); + await fixture.whenStable(); + + const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + saveButton.dispatchEvent(new Event('click')); + fixture.detectChanges(); + afterClosedSubject.next({ + action: TaskFilterDialogCloudComponent.ACTION_SAVE, + icon: 'icon', + name: 'fake-name' + }); + await fixture.whenStable(); + + expect(service.addFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); + expect(dialog.open).toHaveBeenCalled(); + }); + + it('should call restore default filters service on deletion of last filter', async () => { component.toggleFilterActions = true; spyOn(service, 'deleteFilter').and.returnValue(of([])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([])); @@ -715,17 +720,16 @@ describe('EditServiceTaskFilterCloudComponent', () => { stateElement.click(); fixture.detectChanges(); const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(deleteButton.disabled).toBe(false); - deleteButton.click(); - expect(service.deleteFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - expect(restoreDefaultFiltersSpy).toHaveBeenCalled(); - }); - })); + await fixture.whenStable(); + fixture.detectChanges(); + expect(deleteButton.disabled).toBe(false); + deleteButton.click(); + expect(service.deleteFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); + expect(restoreDefaultFiltersSpy).toHaveBeenCalled(); + }); - it('should not call restore default filters service on deletion of first filter', fakeAsync(() => { + it('should not call restore default filters service on deletion of first filter', async () => { component.toggleFilterActions = true; spyOn(service, 'deleteFilter').and.returnValue(of([{ name: 'mock-filter-name' }])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([])); @@ -737,14 +741,13 @@ describe('EditServiceTaskFilterCloudComponent', () => { stateElement.click(); fixture.detectChanges(); const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(deleteButton.disabled).toBe(false); - deleteButton.click(); - expect(service.deleteFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - expect(restoreDefaultFiltersSpy).not.toHaveBeenCalled(); - }); - })); + await fixture.whenStable(); + fixture.detectChanges(); + expect(deleteButton.disabled).toBe(false); + deleteButton.click(); + expect(service.deleteFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); + expect(restoreDefaultFiltersSpy).not.toHaveBeenCalled(); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts index 887ac4992a..5273fb1a95 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts @@ -15,12 +15,12 @@ * limitations under the License. */ -import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material/dialog'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service'; import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service'; @@ -54,6 +54,8 @@ import { } from '../../mock/edit-task-filter-cloud.mock'; import { mockFoodUsers } from '../../../../people/mock/people-cloud.mock'; import { mockFoodGroups } from '../../../../group/mock/group-cloud.mock'; +import { SimpleChanges } from '@angular/core'; +import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component'; describe('EditTaskFilterCloudComponent', () => { let component: EditTaskFilterCloudComponent; @@ -65,6 +67,7 @@ describe('EditTaskFilterCloudComponent', () => { let getTaskFilterSpy: jasmine.Spy; let getRunningApplicationsSpy: jasmine.Spy; let taskService: TaskCloudService; + const afterClosedSubject = new Subject(); setupTestBed({ imports: [ @@ -88,13 +91,10 @@ describe('EditTaskFilterCloudComponent', () => { taskService = TestBed.inject(TaskCloudService); alfrescoApiService = TestBed.inject(AlfrescoApiService); dialog = TestBed.inject(MatDialog); - spyOn(dialog, 'open').and.returnValue({ - afterClosed: of({ - action: EditTaskFilterCloudComponent.ACTION_SAVE, - icon: 'icon', - name: 'fake-name' - }) - } as any); + const dialogRefMock: any = { + afterClosed: () => afterClosedSubject + }; + spyOn(dialog, 'open').and.returnValue(dialogRefMock); spyOn(alfrescoApiService, 'getInstance').and.returnValue(mockAlfrescoApi); getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter)); getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance)); @@ -103,17 +103,17 @@ describe('EditTaskFilterCloudComponent', () => { afterEach(() => fixture.destroy()); - it('should fetch task filter by taskId', () => { + it('should fetch task filter by taskId', async () => { component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(getTaskFilterSpy).toHaveBeenCalled(); - expect(component.taskFilter.name).toEqual('FakeInvolvedTasks'); - expect(component.taskFilter.icon).toEqual('adjust'); - expect(component.taskFilter.status).toEqual(TaskStatusFilter.CREATED); - expect(component.taskFilter.order).toEqual('ASC'); - expect(component.taskFilter.sort).toEqual('id'); - }); + await fixture.whenStable(); + + expect(getTaskFilterSpy).toHaveBeenCalled(); + expect(component.taskFilter.name).toEqual('FakeInvolvedTasks'); + expect(component.taskFilter.icon).toEqual('adjust'); + expect(component.taskFilter.status).toEqual(TaskStatusFilter.CREATED); + expect(component.taskFilter.order).toEqual('ASC'); + expect(component.taskFilter.sort).toEqual('id'); }); describe('processInstanceId filter', () => { @@ -192,7 +192,6 @@ describe('EditTaskFilterCloudComponent', () => { expect(getProcessInstanceIdInputElement().value).toEqual('fakeProcessInstanceIdFromResponse'); }); - }); it('should fetch process definitions when processDefinitionName filter property is set', async () => { @@ -956,36 +955,39 @@ describe('EditTaskFilterCloudComponent', () => { describe('edit filter actions', () => { beforeEach(() => { - component.ngOnChanges({ id: mockTaskFilterIdChange }); - fixture.detectChanges(); - spyOn(component.action, 'emit').and.callThrough(); + component.changedTaskFilter = { name: 'mock-filter-name' } as TaskFilterCloudModel; + component.ngOnChanges({ id: mockTaskFilterIdChange } as SimpleChanges); + spyOn(component.action, 'emit').and.stub(); + component.toggleFilterActions = true; }); it('should emit save event and save the filter on click save button', fakeAsync(() => { - component.toggleFilterActions = true; - spyOn(service, 'updateFilter').and.returnValue(of(null)); + spyOn(service, 'updateFilter').and.returnValue(of([new TaskFilterCloudModel({ name: 'mock-filter-name' })])); fixture.detectChanges(); + const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); + tick(); const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); - const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); + tick(); + const sortOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-sort"] .mat-option-ripple')); sortOptions[3].nativeElement.click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); - expect(saveButton.disabled).toBe(false); - saveButton.click(); - expect(service.updateFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - }); + tick(550); + + const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + saveButton.dispatchEvent(new Event('click')); + fixture.detectChanges(); + tick(); + + expect(service.updateFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); })); it('should emit delete event and delete the filter on click of delete button', async () => { - component.toggleFilterActions = true; spyOn(service, 'deleteFilter').and.returnValue(of(null)); fixture.detectChanges(); const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); @@ -998,38 +1000,45 @@ describe('EditTaskFilterCloudComponent', () => { await fixture.whenStable(); const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - expect(deleteButton.disabled).toBe(false); + expect(deleteButton.getAttribute('disabled')).toBeNull(); deleteButton.click(); expect(service.deleteFilter).toHaveBeenCalled(); expect(component.action.emit).toHaveBeenCalled(); }); - it('should emit saveAs event and add filter on click saveAs button', fakeAsync(() => { - component.toggleFilterActions = true; + it('should emit saveAs event and add filter on click saveAs button', async () => { spyOn(service, 'addFilter').and.returnValue(of(null)); fixture.detectChanges(); + const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); - sortElement.click(); - fixture.detectChanges(); - const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); - sortOptions[2].nativeElement.click(); + const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + stateElement.click(); fixture.detectChanges(); - fixture.whenStable().then(() => { - const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); - expect(saveAsButton.disabled).toBe(false); - saveAsButton.click(); - expect(service.addFilter).toHaveBeenCalled(); - expect(component.action.emit).toHaveBeenCalled(); - expect(dialog.open).toHaveBeenCalled(); + + const sortOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-sort"] .mat-option-ripple')); + sortOptions[3].nativeElement.click(); + fixture.detectChanges(); + await fixture.whenStable(); + + const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + saveButton.dispatchEvent(new Event('click')); + fixture.detectChanges(); + afterClosedSubject.next({ + action: TaskFilterDialogCloudComponent.ACTION_SAVE, + icon: 'icon', + name: 'fake-name' }); - })); + await fixture.whenStable(); + + expect(service.addFilter).toHaveBeenCalled(); + expect(component.action.emit).toHaveBeenCalled(); + expect(dialog.open).toHaveBeenCalled(); + }); it('should call restore default filters service on deletion of last filter', async () => { - component.toggleFilterActions = true; spyOn(service, 'deleteFilter').and.returnValue(of([])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([])); fixture.detectChanges(); @@ -1051,7 +1060,6 @@ describe('EditTaskFilterCloudComponent', () => { }); it('should not call restore default filters service on deletion of first filter', async () => { - component.toggleFilterActions = true; spyOn(service, 'deleteFilter').and.returnValue(of([new TaskFilterCloudModel({ name: 'mock-filter-name' })])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([])); fixture.detectChanges(); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts index 578a3da9c4..6f26d91499 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts @@ -98,7 +98,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone } } - protected updateFilter(filterToUpdate: TaskFilterCloudModel): Observable { + protected updateFilter(filterToUpdate: TaskFilterCloudModel): Observable { return this.taskFilterCloudService.updateFilter(filterToUpdate); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts index b49b828b91..6a4e038621 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts @@ -85,6 +85,10 @@ describe('TaskFormCloudComponent', () => { component = fixture.componentInstance; }); + afterEach(() => { + fixture.destroy(); + }); + describe('Complete button', () => { beforeEach(() => { @@ -285,57 +289,62 @@ describe('TaskFormCloudComponent', () => { beforeEach(() => { component.appName = 'app1'; component.taskId = 'task1'; + fixture.detectChanges(); }); - it('should emit cancelClick when cancel button is clicked', (done) => { - component.cancelClick.subscribe(() => { - done(); - }); + it('should emit cancelClick when cancel button is clicked', async () => { + spyOn(component.cancelClick,'emit').and.stub(); fixture.detectChanges(); const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task')); + cancelBtn.triggerEventHandler('click', {}); + fixture.detectChanges(); + await fixture.whenStable(); - cancelBtn.nativeElement.click(); + expect(component.cancelClick.emit).toHaveBeenCalledOnceWith('task1'); }); - it('should emit taskCompleted when task is completed', (done) => { + it('should emit taskCompleted when task is completed', async () => { spyOn(taskCloudService, 'completeTask').and.returnValue(of({})); - - component.taskCompleted.subscribe(() => { - done(); - }); + spyOn(component.taskCompleted, 'emit').and.stub(); component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); fixture.detectChanges(); - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - completeBtn.nativeElement.click(); + const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); + completeBtn.triggerEventHandler('click', {}); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.taskCompleted.emit).toHaveBeenCalledOnceWith('task1'); }); - it('should emit taskClaimed when task is claimed', (done) => { + it('should emit taskClaimed when task is claimed', async () => { spyOn(taskCloudService, 'claimTask').and.returnValue(of({})); spyOn(component, 'hasCandidateUsers').and.returnValue(true); + spyOn(component.taskClaimed, 'emit').and.stub(); taskDetails.status = TASK_CREATED_STATE; taskDetails.permissions = [TASK_CLAIM_PERMISSION]; getTaskSpy.and.returnValue(of(taskDetails)); - component.taskClaimed.subscribe(() => { - done(); - }); - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); fixture.detectChanges(); const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); + claimBtn.triggerEventHandler('click', {}); + fixture.detectChanges(); + await fixture.whenStable(); - claimBtn.nativeElement.click(); + expect(component.taskClaimed.emit).toHaveBeenCalledOnceWith('task1'); }); - it('should emit error when error occurs', (done) => { - component.error.subscribe(() => { - done(); - }); + it('should emit error when error occurs', async () => { + spyOn(component.error, 'emit').and.stub(); component.onError({}); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.error.emit).toHaveBeenCalled(); }); it('should reload when task is completed', () => { diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts index 76a07b791f..bee6a286f0 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts @@ -16,9 +16,20 @@ */ import { Component, SimpleChange, ViewChild } from '@angular/core'; -import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow, User, DataColumn, ColumnsSelectorComponent } from '@alfresco/adf-core'; +import { AppConfigService, + setupTestBed, + DataRowEvent, + ObjectDataRow, + User, + DataColumn, + ColumnsSelectorComponent, + AlfrescoApiService, + AlfrescoApiServiceMock, + AppConfigServiceMock, + TranslationService, + TranslationMock } from '@alfresco/adf-core'; import { TaskListCloudService } from '../services/task-list-cloud.service'; import { TaskListCloudComponent } from './task-list-cloud.component'; import { fakeGlobalTasks, fakeCustomSchema, fakeGlobalTask } from '../mock/fake-task-response.mock'; @@ -29,6 +40,9 @@ import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.mod import { shareReplay, skip } from 'rxjs/operators'; import { TaskListCloudServiceInterface } from '../../../services/task-list-cloud.service.interface'; import { TASK_LIST_CLOUD_TOKEN } from '../../../services/cloud-token.service'; +import { TaskListCloudModule } from '../task-list-cloud.module'; +import { HttpClientModule } from '@angular/common/http'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @Component({ template: ` @@ -88,9 +102,6 @@ describe('TaskListCloudComponent', () => { TranslateModule.forRoot(), ProcessServiceCloudTestingModule ], - declarations: [ - EmptyTemplateComponent - ], providers: [ { provide: TASK_LIST_CLOUD_TOKEN, @@ -523,6 +534,20 @@ describe('TaskListCloudComponent', () => { describe('Creating an empty custom template - EmptyTemplateComponent', () => { let fixtureEmpty: ComponentFixture; + setupTestBed({ + imports: [ + HttpClientModule, + NoopAnimationsModule, + TranslateModule.forRoot(), + TaskListCloudModule + ], + providers: [ + { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, + { provide: AppConfigService, useClass: AppConfigServiceMock }, + { provide: TranslationService, useClass: TranslationMock } + ] + }); + beforeEach(() => { const emptyList = { list: { entries: [] } }; spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList)); @@ -535,16 +560,12 @@ describe('TaskListCloudComponent', () => { fixtureEmpty.destroy(); }); - // TODO still not working because of the Loading Spinner - // eslint-disable-next-line - xit('should render the custom template', (done) => { + it('should render the custom template', async () => { fixtureEmpty.detectChanges(); - fixtureEmpty.whenStable().then(() => { - fixtureEmpty.detectChanges(); - expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull(); - expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); - done(); - }); + await fixtureEmpty.whenStable(); + fixtureEmpty.detectChanges(); + expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull(); + expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); }); }); @@ -581,7 +602,7 @@ describe('TaskListCloudComponent', () => { sortable: true }, { - key: 'entry.priority', + key: 'priority', type: 'text', title: 'ADF_TASK_LIST.PROPERTIES.PRIORITY', sortable: true @@ -602,45 +623,34 @@ describe('TaskListCloudComponent', () => { fixture.destroy(); }); - // TODO: highly unstable test - // eslint-disable-next-line - xit('should show tooltip if config copyContent flag is true', fakeAsync(() => { + it('should show tooltip if config copyContent flag is true', fakeAsync(() => { taskSpy.and.returnValue(of(fakeGlobalTasks)); const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - component.success.subscribe(() => { - fixture.whenStable().then(() => { - fixture.detectChanges(); - const spanHTMLElement = element.querySelector('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'); - spanHTMLElement.dispatchEvent(new Event('mouseenter')); - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull(); - }); - }); - component.presetColumn = 'fakeCustomSchema'; component.appName = appName.currentValue; component.ngOnChanges({ appName }); component.ngAfterContentInit(); + + tick(); + fixture.detectChanges(); + const spanHTMLElement = element.querySelector('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'); + spanHTMLElement.dispatchEvent(new Event('mouseenter')); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull(); })); - // TODO: highly unstable test - // eslint-disable-next-line - xit('should replace priority values', (done) => { + it('should replace priority values', fakeAsync(() => { taskSpy.and.returnValue(of(fakeGlobalTasks)); component.presetColumn = 'fakeCustomSchema'; const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); component.ngOnChanges({ appName }); - - component.success.subscribe(() => { - const cell = fixture.nativeElement.querySelector('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]'); - expect(cell.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE'); - done(); - }); - fixture.detectChanges(); component.reload(); - }); + tick(); + const cell = fixture.nativeElement.querySelector('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]'); + expect(cell.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE'); + })); it('replacePriorityValues should return undefined when no rows defined', () => { const emptyList = { list: { entries: [] } }; diff --git a/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts index 15dd42f8da..06e9c88aa6 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts @@ -132,39 +132,43 @@ describe('Activiti ServiceTaskList Cloud Service', () => { }); - it('should throw an exeption and execute logService error if appName is null', () => { + it('should throw an exeption and execute logService error if appName is null', (done) => { const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured'; const params = [null, 'executionId_1', 'flowNodeId_1'] as const; service.replayServiceTaskRequest(...params).toPromise().catch((error) => { expect(error).toEqual(expectedErrorMessage); expect(logServiceErrorSpy).toHaveBeenCalled(); + done(); }); }); - it('should throw an exeption and execute logService error if executionId is null', () => { + it('should throw an exeption and execute logService error if executionId is null', (done) => { const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured'; const params = ['fakeName', null, 'flowNodeId_1'] as const; service.replayServiceTaskRequest(...params).toPromise().catch((error) => { expect(error).toEqual(expectedErrorMessage); expect(logServiceErrorSpy).toHaveBeenCalled(); + done(); }); }); - it('should throw an exeption and execute logService error if flowNodeId is null', () => { + it('should throw an exeption and execute logService error if flowNodeId is null', (done) => { const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured'; const params = ['fakeName', 'executionId_1', null] as const; service.replayServiceTaskRequest(...params).toPromise().catch((error) => { expect(error).toEqual(expectedErrorMessage); expect(logServiceErrorSpy).toHaveBeenCalled(); + done(); }); }); - it('should throw an exeption and execute logService error if appName, executionId and flowNodeId are null', () => { + it('should throw an exeption and execute logService error if appName, executionId and flowNodeId are null', (done) => { const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured'; const params = [null, null, null] as const; service.replayServiceTaskRequest(...params).toPromise().catch((error) => { expect(error).toEqual(expectedErrorMessage); expect(logServiceErrorSpy).toHaveBeenCalled(); + done(); }); }); });