[ACS-6140] migrate tests to harness (#9071)

* migrate aspect list tests to harness

* [ci:force] categories management

* [ci:force] fix keyboard handling

* [ci:force] fix memory leak

* migrate document list

* migrate new version uploader dialog

* migrate folder dialog

* bug fixes for keyboard, remove fake unit tests

* [ci:force] migrate and cleanup notification tests

* migrate app details cloud

* migrate app list cloud

* migrate group cloud component

* [ci:force] cleanup code based on reviews

* [ci:force] migrate people cloud

* [ci:force] migrate process list cloud

* [ci:force] migrate start process cloud

* [ci:force] task form cloud

* [ci:force] service task list cloud

* [ci:force] task list cloud

* [ci:force] process attachment list and apps list

* [ci:force] code review changes

* [ci:force] app list bug fixes and code cleanup

* [ci:force] fix incorrect/missing typings, fix tests

* [ci:force] code cleanup
This commit is contained in:
Denys Vuika
2023-11-09 09:24:56 +00:00
committed by GitHub
parent 479cc8b545
commit f0a11fdab0
35 changed files with 941 additions and 1231 deletions

View File

@@ -39,6 +39,6 @@
<ng-template #loading>
<div class="adf-aspect-list-spinner">
<mat-spinner id="adf-aspect-spinner"></mat-spinner>
<mat-progress-spinner mode="indeterminate" id="adf-aspect-spinner"></mat-progress-spinner>
</div>
</ng-template>

View File

@@ -24,86 +24,96 @@ import { AspectListService } from './services/aspect-list.service';
import { of } from 'rxjs';
import { AspectEntry } from '@alfresco/js-api';
import { delay } from 'rxjs/operators';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
import { MatTableHarness } from '@angular/material/table/testing';
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
const aspectListMock: AspectEntry[] = [{
entry: {
parentId: 'frs:aspectZero',
id: 'frs:AspectOne',
description: 'First Aspect with random description',
title: 'FirstAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:propB'
}
]
const aspectListMock: AspectEntry[] = [
{
entry: {
parentId: 'frs:aspectZero',
id: 'frs:AspectOne',
description: 'First Aspect with random description',
title: 'FirstAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:propB'
}
]
}
},
{
entry: {
parentId: 'frs:AspectZer',
id: 'frs:SecondAspect',
description: 'Second Aspect description',
title: 'SecondAspect',
properties: [
{
id: 'assetId',
title: 'Published Asset Id',
dataType: 'd:text'
},
{
id: 'assetUrl',
title: 'Published Asset URL',
dataType: 'd:text'
}
]
}
}
},
{
entry: {
parentId: 'frs:AspectZer',
id: 'frs:SecondAspect',
description: 'Second Aspect description',
title: 'SecondAspect',
properties: [
{
id: 'assetId',
title: 'Published Asset Id',
dataType: 'd:text'
},
{
id: 'assetUrl',
title: 'Published Asset URL',
dataType: 'd:text'
}
]
}
}];
];
const customAspectListMock: AspectEntry[] = [{
entry: {
parentId: 'cst:parentAspect',
id: 'cst:customAspect',
description: 'Custom Aspect with random description',
title: 'CustomAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:propB'
}
]
const customAspectListMock: AspectEntry[] = [
{
entry: {
parentId: 'cst:parentAspect',
id: 'cst:customAspect',
description: 'Custom Aspect with random description',
title: 'CustomAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:propB'
}
]
}
},
{
entry: {
parentId: 'cst:commonaspect',
id: 'cst:nonamedAspect',
description: '',
title: '',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
}
]
}
}
},
{
entry: {
parentId: 'cst:commonaspect',
id: 'cst:nonamedAspect',
description: '',
title: '',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
}
]
}
}];
];
describe('AspectListComponent', () => {
let loader: HarnessLoader;
let component: AspectListComponent;
let fixture: ComponentFixture<AspectListComponent>;
let aspectListService: AspectListService;
@@ -111,36 +121,31 @@ describe('AspectListComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule],
providers: [AspectListService]
});
});
describe('Loading', () => {
beforeEach(() => {
fixture = TestBed.createComponent(AspectListComponent);
component = fixture.componentInstance;
nodeService = TestBed.inject(NodesApiService);
aspectListService = TestBed.inject(AspectListService);
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should show the loading spinner when result is loading', () => {
it('should show the loading spinner when result is loading', async () => {
const delayResult = of(null).pipe(delay(0));
spyOn(nodeService, 'getNode').and.returnValue(delayResult);
spyOn(aspectListService, 'getAspects').and.returnValue(delayResult);
fixture.detectChanges();
const spinner = fixture.nativeElement.querySelector('#adf-aspect-spinner');
expect(spinner).toBeDefined();
expect(spinner).not.toBeNull();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
});
});
describe('When passing a node id', () => {
beforeEach(() => {
fixture = TestBed.createComponent(AspectListComponent);
component = fixture.componentInstance;
@@ -152,6 +157,7 @@ describe('AspectListComponent', () => {
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node-id', aspectNames: ['frs:AspectOne'] } as any));
component.nodeId = 'fake-node-id';
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -167,106 +173,80 @@ describe('AspectListComponent', () => {
expect(component.hasEqualAspect).toBe(false);
});
it('should show all the aspects', () => {
const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect');
const secondElement = fixture.nativeElement.querySelector('#aspect-list-SecondAspect');
expect(firstElement).not.toBeNull();
expect(firstElement).toBeDefined();
expect(secondElement).not.toBeNull();
expect(secondElement).toBeDefined();
it('should show all the aspects', async () => {
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-FirstAspect' }))).toBe(true);
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-SecondAspect' }))).toBe(true);
});
it('should show aspect id when name or title is not set', () => {
const noNameAspect: HTMLElement = fixture.nativeElement.querySelector('#aspect-list-cst-nonamedAspect .adf-aspect-list-element-title');
const noNameAspect = fixture.nativeElement.querySelector('#aspect-list-cst-nonamedAspect .adf-aspect-list-element-title');
expect(noNameAspect).toBeDefined();
expect(noNameAspect).not.toBeNull();
expect(noNameAspect.innerText).toBe('cst:nonamedAspect');
});
it('should show the details when a row is clicked', () => {
const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect');
firstElement.click();
fixture.detectChanges();
const firstElementDesc = fixture.nativeElement.querySelector('#aspect-list-0-description');
expect(firstElementDesc).not.toBeNull();
expect(firstElementDesc).toBeDefined();
it('should show the details when a row is clicked', async () => {
const panel = await loader.getHarness(MatExpansionPanelHarness);
await panel.expand();
expect(await panel.getDescription()).not.toBeNull();
const firstElementPropertyTable = fixture.nativeElement.querySelector('#aspect-list-0-properties-table');
expect(firstElementPropertyTable).not.toBeNull();
expect(firstElementPropertyTable).toBeDefined();
const nameProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-name');
expect(nameProperties[0]).not.toBeNull();
expect(nameProperties[0]).toBeDefined();
expect(nameProperties[0].innerText).toBe('channelPassword');
expect(nameProperties[1]).not.toBeNull();
expect(nameProperties[1]).toBeDefined();
expect(nameProperties[1].innerText).toBe('channelUsername');
const table = await panel.getHarness(MatTableHarness);
const [row1, row2] = await table.getRows();
const [r1c1, r1c2, r1c3] = await row1.getCells();
expect(await r1c1.getText()).toBe('channelPassword');
expect(await r1c2.getText()).toBe('The authenticated channel password');
expect(await r1c3.getText()).toBe('d:propA');
const titleProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-title');
expect(titleProperties[0]).not.toBeNull();
expect(titleProperties[0]).toBeDefined();
expect(titleProperties[0].innerText).toBe('The authenticated channel password');
expect(titleProperties[1]).not.toBeNull();
expect(titleProperties[1]).toBeDefined();
expect(titleProperties[1].innerText).toBe('The authenticated channel username');
const dataTypeProperties = fixture.nativeElement.querySelectorAll('#aspect-list-0-properties-table tbody .mat-column-dataType');
expect(dataTypeProperties[0]).not.toBeNull();
expect(dataTypeProperties[0]).toBeDefined();
expect(dataTypeProperties[0].innerText).toBe('d:propA');
expect(dataTypeProperties[1]).not.toBeNull();
expect(dataTypeProperties[1]).toBeDefined();
expect(dataTypeProperties[1].innerText).toBe('d:propB');
const [r2c1, r2c2, r2c3] = await row2.getCells();
expect(await r2c1.getText()).toBe('channelUsername');
expect(await r2c2.getText()).toBe('The authenticated channel username');
expect(await r2c3.getText()).toBe('d:propB');
});
it('should show as checked the node properties', () => {
const firstAspectCheckbox: HTMLInputElement = fixture.nativeElement.querySelector('#aspect-list-0-check-input');
expect(firstAspectCheckbox).toBeDefined();
expect(firstAspectCheckbox).not.toBeNull();
expect(firstAspectCheckbox.checked).toBeTruthy();
it('should show as checked the node properties', async () => {
const panel = await loader.getHarness(MatExpansionPanelHarness);
await panel.expand();
const checkbox = await panel.getHarness(MatCheckboxHarness);
expect(await checkbox.isChecked()).toBe(true);
});
it('should remove aspects unchecked', (done) => {
const secondElement = fixture.nativeElement.querySelector('#aspect-list-1-check-input');
expect(secondElement).toBeDefined();
expect(secondElement).not.toBeNull();
expect(secondElement.checked).toBeFalsy();
secondElement.click();
fixture.detectChanges();
it('should remove aspects unchecked', async () => {
const panel = await loader.getAllHarnesses(MatExpansionPanelHarness);
await panel[1].expand();
const checkbox = await panel[1].getHarness(MatCheckboxHarness);
expect(await checkbox.isChecked()).toBe(false);
await checkbox.toggle();
expect(component.nodeAspects.length).toBe(2);
expect(component.nodeAspects[1]).toBe('frs:SecondAspect');
component.valueChanged.subscribe((aspects) => {
expect(aspects.length).toBe(1);
expect(aspects[0]).toBe('frs:AspectOne');
done();
});
secondElement.click();
fixture.detectChanges();
});
it('should reset the properties on reset', (done) => {
const secondElement = fixture.nativeElement.querySelector('#aspect-list-1-check-input');
expect(secondElement).toBeDefined();
expect(secondElement).not.toBeNull();
expect(secondElement.checked).toBeFalsy();
secondElement.click();
fixture.detectChanges();
expect(component.nodeAspects.length).toBe(2);
component.valueChanged.subscribe((aspects) => {
expect(aspects.length).toBe(1);
done();
});
component.reset();
});
await checkbox.toggle();
it('should clear all the properties on clear', (done) => {
expect(component.nodeAspects.length).toBe(1);
component.valueChanged.subscribe((aspects) => {
expect(aspects.length).toBe(0);
done();
});
expect(component.nodeAspects[0]).toBe('frs:AspectOne');
});
it('should reset the properties on reset', async () => {
const panel = await loader.getAllHarnesses(MatExpansionPanelHarness);
await panel[1].expand();
const checkbox = await panel[1].getHarness(MatCheckboxHarness);
expect(await checkbox.isChecked()).toBe(false);
await checkbox.toggle();
expect(component.nodeAspects.length).toBe(2);
component.reset();
expect(component.nodeAspects.length).toBe(1);
});
it('should clear all the properties on clear', async () => {
expect(component.nodeAspects.length).toBe(1);
component.clear();
expect(component.nodeAspects.length).toBe(0);
});
});
@@ -277,21 +257,16 @@ describe('AspectListComponent', () => {
aspectListService = TestBed.inject(AspectListService);
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
fixture.destroy();
});
it('should show all the aspects', () => {
const firstElement = fixture.nativeElement.querySelector('#aspect-list-FirstAspect');
const secondElement = fixture.nativeElement.querySelector('#aspect-list-SecondAspect');
expect(firstElement).not.toBeNull();
expect(firstElement).toBeDefined();
expect(secondElement).not.toBeNull();
expect(secondElement).toBeDefined();
it('should show all the aspects', async () => {
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-FirstAspect' }))).toBe(true);
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-SecondAspect' }))).toBe(true);
});
});
});

View File

@@ -71,7 +71,8 @@
[value]="category">
{{ category.name }}
</mat-list-option>
<p *ngIf="!existingCategories?.length && !existingCategoriesLoading">
<p *ngIf="!existingCategories?.length && !existingCategoriesLoading"
data-automation-id="no-categories-message">
{{ 'CATEGORIES_MANAGEMENT.NO_EXISTING_CATEGORIES' | translate }}
</p>
</mat-selection-list>

View File

@@ -16,7 +16,6 @@
*/
import { Category, CategoryPaging, ResultNode, ResultSetPaging } from '@alfresco/js-api';
import { DebugElement } from '@angular/core';
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { Validators } from '@angular/forms';
import { MatError } from '@angular/material/form-field';
@@ -28,8 +27,12 @@ import { ContentTestingModule } from '../../testing/content.testing.module';
import { CategoriesManagementMode } from './categories-management-mode';
import { CategoryService } from '../services/category.service';
import { CategoriesManagementComponent } from './categories-management.component';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
describe('CategoriesManagementComponent', () => {
let loader: HarnessLoader;
let component: CategoriesManagementComponent;
let fixture: ComponentFixture<CategoriesManagementComponent>;
let categoryService: CategoryService;
@@ -38,18 +41,15 @@ describe('CategoriesManagementComponent', () => {
const category2 = new Category({ id: 'test2', name: 'testCat2' });
const category3 = new Category({ id: 'test3', name: 'testCat3' });
const category4 = new Category({ id: 'test4', name: 'testCat4' });
const resultCat1 = new ResultNode({ id: 'test', name: 'testCat', path: { name: 'general/categories' }});
const resultCat2 = new ResultNode({ id: 'test2', name: 'testCat2', path: { name: 'general/categories' }});
const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [ { entry: category1 }, { entry: category2 }]}};
const categorySearchResponse: ResultSetPaging = { list: { pagination: {}, entries: [ { entry: resultCat1 }, { entry: resultCat2 }]}};
const resultCat1 = new ResultNode({ id: 'test', name: 'testCat', path: { name: 'general/categories' } });
const resultCat2 = new ResultNode({ id: 'test2', name: 'testCat2', path: { name: 'general/categories' } });
const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [{ entry: category1 }, { entry: category2 }] } };
const categorySearchResponse: ResultSetPaging = { list: { pagination: {}, entries: [{ entry: resultCat1 }, { entry: resultCat2 }] } };
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [CategoriesManagementComponent],
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule],
providers: [
{
provide: CategoryService,
@@ -65,6 +65,7 @@ describe('CategoriesManagementComponent', () => {
fixture = TestBed.createComponent(CategoriesManagementComponent);
component = fixture.componentInstance;
categoryService = TestBed.inject(CategoryService);
loader = TestbedHarnessEnvironment.loader(fixture);
});
/**
@@ -138,7 +139,9 @@ describe('CategoriesManagementComponent', () => {
* @returns list of native elements
*/
function getRemoveCategoryButtons(): HTMLButtonElement[] {
return fixture.debugElement.queryAll(By.css(`[data-automation-id="categories-remove-category-button"]`)).map((debugElem) => debugElem.nativeElement);
return fixture.debugElement
.queryAll(By.css(`[data-automation-id="categories-remove-category-button"]`))
.map((debugElem) => debugElem.nativeElement);
}
/**
@@ -261,39 +264,28 @@ describe('CategoriesManagementComponent', () => {
});
describe('Spinner', () => {
/**
* Get the spinner element
*
* @returns debug element
*/
function getSpinner(): DebugElement {
return fixture.debugElement.query(By.css(`.mat-progress-spinner`));
}
it('should not be displayed when existing categories stopped loading', async () => {
component.categoryNameControlVisible = true;
fixture.detectChanges();
it('should be displayed with correct diameter when existing categories are loading', fakeAsync(() => {
typeCategory('Category 1', 0);
const categoryControlInput = getCategoryControlInput();
categoryControlInput.value = 'Category 1';
categoryControlInput.dispatchEvent(new InputEvent('input'));
const spinner = getSpinner();
expect(spinner).toBeTruthy();
expect(spinner.componentInstance.diameter).toBe(50);
fixture.detectChanges();
await fixture.whenStable();
discardPeriodicTasks();
flush();
}));
it('should not be displayed when existing categories stopped loading', fakeAsync(() => {
typeCategory('Category 1');
const spinner = getSpinner();
expect(spinner).toBeFalsy();
}));
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
});
});
it('should display correct message when there are no existing categories', fakeAsync(() => {
spyOn(categoryService, 'getSubcategories').and.returnValue(of({list: { pagination: {}, entries: []}}));
spyOn(categoryService, 'getSubcategories').and.returnValue(of({ list: { pagination: {}, entries: [] } }));
typeCategory('test');
const noExistingCategoriesMsg = fixture.debugElement.query(By.css('mat-selection-list p'))?.nativeElement.textContent.trim();
const noExistingCategoriesMsg = fixture.debugElement
.query(By.css(`[data-automation-id="no-categories-message"]`))
?.nativeElement.textContent.trim();
expect(noExistingCategoriesMsg).toBe('CATEGORIES_MANAGEMENT.NO_EXISTING_CATEGORIES');
}));
});
@@ -328,7 +320,9 @@ describe('CategoriesManagementComponent', () => {
it('should have correct remove category title', () => {
const removeButtons = getRemoveCategoryButtons();
const isTitleCorrect = removeButtons.every((removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.UNASSIGN_CATEGORY');
const isTitleCorrect = removeButtons.every(
(removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.UNASSIGN_CATEGORY'
);
expect(isTitleCorrect).toBeTrue();
});
@@ -434,7 +428,9 @@ describe('CategoriesManagementComponent', () => {
it('should have correct remove category title', () => {
const removeButtons = getRemoveCategoryButtons();
const isTitleCorrect = removeButtons.every((removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.DELETE_CATEGORY');
const isTitleCorrect = removeButtons.every(
(removeBtn) => removeBtn.attributes.getNamedItem('title').textContent === 'CATEGORIES_MANAGEMENT.DELETE_CATEGORY'
);
expect(isTitleCorrect).toBeTrue();
});

View File

@@ -15,9 +15,9 @@
* limitations under the License.
*/
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, Inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { TranslationService, NotificationService} from '@alfresco/adf-core';
import { TranslationService, NotificationService } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { AllowableOperationsEnum } from '../common/models/allowable-operations.enum';
import { ContentService } from '../common/services/content.service';
@@ -26,6 +26,8 @@ import { ContentNodeSelectorComponentData } from './content-node-selector.compon
import { NodeEntryEvent } from '../document-list/components/node.event';
import { NodeAction } from '../document-list/models/node-action.enum';
import { OverlayContainer } from '@angular/cdk/overlay';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'adf-content-node-selector',
@@ -33,7 +35,9 @@ import { OverlayContainer } from '@angular/cdk/overlay';
styleUrls: ['./content-node-selector.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ContentNodeSelectorComponent implements OnInit {
export class ContentNodeSelectorComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<void>();
title: string;
action: NodeAction;
buttonActionName: string;
@@ -48,13 +52,15 @@ export class ContentNodeSelectorComponent implements OnInit {
emptyFolderImageUrl: string = './assets/images/empty_doc_lib.svg';
breadcrumbFolderNode: Node;
constructor(private translation: TranslationService,
private contentService: ContentService,
private notificationService: NotificationService,
private uploadService: UploadService,
private dialog: MatDialogRef<ContentNodeSelectorComponent>,
private overlayContainer: OverlayContainer,
@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) {
constructor(
private translation: TranslationService,
private contentService: ContentService,
private notificationService: NotificationService,
private uploadService: UploadService,
private dialog: MatDialogRef<ContentNodeSelectorComponent>,
private overlayContainer: OverlayContainer,
@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData
) {
this.action = data.actionName ?? NodeAction.CHOOSE;
this.buttonActionName = `NODE_SELECTOR.${this.action}`;
this.title = data.title;
@@ -62,28 +68,41 @@ export class ContentNodeSelectorComponent implements OnInit {
}
ngOnInit() {
this.dialog.keydownEvents().subscribe(event => {
// Esc
if (event.keyCode === 27) {
event.preventDefault();
event.stopImmediatePropagation();
this.dialog
.keydownEvents()
.pipe(takeUntil(this.onDestroy$))
.subscribe((event) => {
if (event?.key === 'Escape') {
event.preventDefault();
event.stopImmediatePropagation();
this.close();
}
});
this.dialog
.backdropClick()
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => {
this.close();
}
});
});
this.dialog.backdropClick().subscribe(() => {
this.close();
});
this.dialog
.afterOpened()
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => {
this.overlayContainer.getContainerElement().setAttribute('role', 'main');
});
this.dialog.afterOpened().subscribe(() => {
this.overlayContainer.getContainerElement().setAttribute('role', 'main');
});
this.uploadService.fileUploadStarting.subscribe(() => {
this.uploadService.fileUploadStarting.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
this.uploadStarted = true;
});
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
close() {
this.dialog.close();
this.overlayContainer.getContainerElement().setAttribute('role', 'region');
@@ -179,16 +198,17 @@ export class ContentNodeSelectorComponent implements OnInit {
}
getWarningMessage(): string {
return this.showingSearch ? 'NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE' :
(this.hasNoPermissionToUpload() ? 'NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE' : '');
if (this.showingSearch) {
return 'NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE';
}
return this.hasNoPermissionToUpload() ? 'NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE' : '';
}
hasNoPermissionToUpload(): boolean {
return (!this.hasAllowableOperations && !this.showingSearch) && !this.isLoading;
return !this.hasAllowableOperations && !this.showingSearch && !this.isLoading;
}
hasUploadError(): boolean {
return this.showingSearch || this.hasNoPermissionToUpload();
}
}

View File

@@ -48,6 +48,8 @@ describe('FolderDialogComponent', () => {
fixture.destroy();
});
const getTitle = () => fixture.debugElement.query(By.css('[data-automation-id="adf-folder-dialog-title"]'));
describe('Edit', () => {
beforeEach(() => {
component.data = {
@@ -70,8 +72,8 @@ describe('FolderDialogComponent', () => {
});
it('should have the proper title', () => {
const title = fixture.debugElement.query(By.css('[mat-dialog-title]'));
expect(title === null).toBe(false);
const title = getTitle();
expect(title).not.toBeNull();
expect(title.nativeElement.innerText.trim()).toBe('CORE.FOLDER_DIALOG.EDIT_FOLDER_TITLE');
});
@@ -165,8 +167,8 @@ describe('FolderDialogComponent', () => {
});
it('should have the proper title', () => {
const title = fixture.debugElement.query(By.css(`[data-automation-id="adf-folder-dialog-title"]`));
expect(title === null).toBe(false);
const title = getTitle();
expect(title).not.toBeNull();
expect(title.nativeElement.innerText.trim()).toBe('CORE.FOLDER_DIALOG.CREATE_FOLDER_TITLE');
});

View File

@@ -62,12 +62,16 @@ import { domSanitizerMock } from '../../testing/dom-sanitizer-mock';
import { MatDialog } from '@angular/material/dialog';
import { FileAutoDownloadComponent } from './file-auto-download/file-auto-download.component';
import { ShareDataTableAdapter } from '../data/share-datatable-adapter';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
const mockDialog = {
open: jasmine.createSpy('open')
};
describe('DocumentList', () => {
let loader: HarnessLoader;
let documentList: DocumentListComponent;
let documentListService: DocumentListService;
let customResourcesService: CustomResourcesService;
@@ -116,6 +120,8 @@ describe('DocumentList', () => {
spyFavorite = spyOn(customResourcesService.favoritesApi, 'listFavorites').and.returnValue(
Promise.resolve(new FavoritePaging({ list: new FavoritePagingList({ entries: [] }) }))
);
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -219,7 +225,7 @@ describe('DocumentList', () => {
it('should show the header when there are no records in the table but filter is active', () => {
documentList.data = new ShareDataTableAdapter(thumbnailService, contentService, []);
documentList.filterValue = { $thumbnail: 'TYPE:"cm:folder"' };
documentList.filterValue = { $thumbnail: 'TYPE:"cm:folder"' };
fixture.detectChanges();
@@ -1075,10 +1081,11 @@ describe('DocumentList', () => {
expect(fixture.debugElement.query(By.css('.adf-no-permission__template'))).not.toBeNull();
});
it('should display loading template when data is loading', () => {
it('should display loading template when data is loading', async () => {
documentList.loading = true;
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('mat-progress-spinner'))).not.toBeNull();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
});
it('should empty folder NOT show the pagination', () => {
@@ -1406,14 +1413,16 @@ describe('DocumentList', () => {
expect(documentList.reload).toHaveBeenCalled();
});
it('should not show loading state if pagination is updated with merge setting as true', fakeAsync (() => {
it('should not show loading state if pagination is updated with merge setting as true', fakeAsync(() => {
spyFolderNode = spyOn(documentListService, 'loadFolderByNodeId').and.callFake(() =>
of(new DocumentLoaderNode(null, {
list: {
pagination: {},
entries: mockPreselectedNodes
}
}))
of(
new DocumentLoaderNode(null, {
list: {
pagination: {},
entries: mockPreselectedNodes
}
})
)
);
fixture.detectChanges();
const fakeDatatableRows = [

View File

@@ -1,4 +1,4 @@
<h1 mat-dialog-title>{{ title | translate }}</h1>
<h1 mat-dialog-title data-automation-id="new-version-uploader-dialog-title">{{ title | translate }}</h1>
<section mat-dialog-content *ngIf="!data.showVersionsOnly">
<adf-version-comparison id="adf-version-comparison" [newFileVersion]="data.file" [node]="data.node"></adf-version-comparison>
<adf-version-upload

View File

@@ -28,13 +28,13 @@ import { NewVersionUploaderDialogComponent } from './new-version-uploader.dialog
describe('NewVersionUploaderDialog', () => {
let component: NewVersionUploaderDialogComponent;
let fixture: ComponentFixture<NewVersionUploaderDialogComponent>;
let nativeElement;
let nativeElement: HTMLElement;
const cssSelectors = {
adfVersionUploadButton: '#adf-version-upload-button',
adfVersionComparison: '#adf-version-comparison',
adfVersionList: '.adf-version-list',
matDialogTitle: '.mat-dialog-title'
title: '[data-automation-id="new-version-uploader-dialog-title"]'
};
const mockDialogRef = {
@@ -45,10 +45,7 @@ describe('NewVersionUploaderDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule],
declarations: [
NewVersionUploaderDialogComponent,
VersionListComponent,
@@ -59,7 +56,8 @@ describe('NewVersionUploaderDialog', () => {
providers: [
{ provide: MAT_DIALOG_DATA, useValue: { node: mockNode, showVersionsOnly, file: mockFile } },
{
provide: MatDialogRef, useValue: mockDialogRef
provide: MatDialogRef,
useValue: mockDialogRef
}
]
});
@@ -72,12 +70,7 @@ describe('NewVersionUploaderDialog', () => {
fixture.destroy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('Upload New Version', () => {
const expectedUploadNewVersionTitle = 'ADF-NEW-VERSION-UPLOADER.DIALOG_UPLOAD.TITLE';
it('should display adf version upload button if showVersionsOnly is passed as false from parent component', () => {
@@ -104,7 +97,7 @@ describe('NewVersionUploaderDialog', () => {
it('should show default title if title is not provided from parent component', () => {
component.data.showVersionsOnly = false;
fixture.detectChanges();
const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle);
const matDialogTitle = nativeElement.querySelector(cssSelectors.title);
expect(matDialogTitle.innerHTML).toEqual(expectedUploadNewVersionTitle);
});
@@ -112,7 +105,7 @@ describe('NewVersionUploaderDialog', () => {
component.data.showVersionsOnly = false;
component.data.title = '';
fixture.detectChanges();
const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle);
const matDialogTitle = nativeElement.querySelector(cssSelectors.title);
expect(matDialogTitle.innerHTML).toEqual(expectedUploadNewVersionTitle);
});
@@ -120,7 +113,7 @@ describe('NewVersionUploaderDialog', () => {
component.data.showVersionsOnly = false;
component.data.title = 'TEST_TITLE';
fixture.detectChanges();
const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle);
const matDialogTitle = nativeElement.querySelector(cssSelectors.title);
expect(matDialogTitle.innerHTML).toEqual('TEST_TITLE');
});
@@ -150,11 +143,9 @@ describe('NewVersionUploaderDialog', () => {
component.handleCancel();
expect(mockDialogRef.close).toHaveBeenCalled();
});
});
describe('Manage Versions', () => {
const expectedManageVersionsTitle = 'ADF-NEW-VERSION-UPLOADER.DIALOG_LIST.TITLE';
it('should display adf version list if showVersionsOnly is passed as true from parent component', () => {
@@ -182,7 +173,7 @@ describe('NewVersionUploaderDialog', () => {
component.data.showVersionsOnly = true;
component.data.title = undefined;
fixture.detectChanges();
const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle);
const matDialogTitle = nativeElement.querySelector(cssSelectors.title);
expect(matDialogTitle.innerHTML).toEqual(expectedManageVersionsTitle);
});
@@ -190,7 +181,7 @@ describe('NewVersionUploaderDialog', () => {
component.data.showVersionsOnly = true;
component.data.title = '';
fixture.detectChanges();
const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle);
const matDialogTitle = nativeElement.querySelector(cssSelectors.title);
expect(matDialogTitle.innerHTML).toEqual(expectedManageVersionsTitle);
});
@@ -198,10 +189,8 @@ describe('NewVersionUploaderDialog', () => {
component.data.showVersionsOnly = true;
component.data.title = 'TEST_TITLE';
fixture.detectChanges();
const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle);
const matDialogTitle = nativeElement.querySelector(cssSelectors.title);
expect(matDialogTitle.innerHTML).toEqual('TEST_TITLE');
});
});
});

View File

@@ -12,7 +12,7 @@
(change)="$event.stopPropagation()"
/>
<button mat-button
<button
matSuffix
mat-icon-button
[attr.aria-label]="'SELECT_FILTER.BUTTON.ARIA_LABEL' | translate"

View File

@@ -19,163 +19,67 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CoreTestingModule } from '../../../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { SelectFilterInputComponent } from './select-filter-input.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component, ViewChild } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ESCAPE } from '@angular/cdk/keycodes';
import { MatSelect } from '@angular/material/select';
@Component({
selector: 'adf-test-filter',
template: `
<mat-select [(ngModel)]="field.value" [compareWith]="compare" [multiple]="multiple">
<adf-select-filter-input *ngIf="showInputFilter" (change)="onChange($event)"></adf-select-filter-input>
<mat-option *ngFor="let opt of options"
[value]="opt"
[id]="opt.id">{{opt.name}}
</mat-option>
</mat-select>
`
})
export class TestComponent {
@ViewChild(SelectFilterInputComponent) filterInputComponent: SelectFilterInputComponent;
field: any = { value : '' };
showInputFilter = true;
multiple = false;
standardOptions = [
{ id: '1', name: 'one' },
{ id: '2', name: 'two' },
{ id: '3', name: 'three' }
];
options = this.standardOptions;
compare(obj1, obj2) {
if (!obj1 || !obj2) {
return false;
}
return obj1.id === obj2.id;
}
onChange(search: string) {
if (!search) {
this.options = this.standardOptions;
} else {
this.options = this.standardOptions.filter(({ name }) => name.includes(search));
}
}
}
describe('SelectFilterInputComponent', () => {
let testFixture: ComponentFixture<TestComponent>;
let testComponent: TestComponent;
let fixture: ComponentFixture<SelectFilterInputComponent>;
let component: SelectFilterInputComponent;
let matSelect: MatSelect;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
NoopAnimationsModule
],
declarations: [
TestComponent
],
providers: [
MatSelect
]
imports: [TranslateModule.forRoot(), CoreTestingModule],
providers: [MatSelect]
});
fixture = TestBed.createComponent(SelectFilterInputComponent);
component = fixture.componentInstance;
matSelect = TestBed.inject(MatSelect);
fixture.detectChanges();
});
describe('component', () => {
it('should focus input on initialization', async () => {
spyOn(component.selectFilterInput.nativeElement, 'focus');
matSelect.openedChange.next(true);
beforeEach(() => {
fixture = TestBed.createComponent(SelectFilterInputComponent);
component = fixture.componentInstance;
matSelect = TestBed.inject(MatSelect);
fixture.detectChanges();
});
it('should focus input on initialization', async () => {
spyOn(component.selectFilterInput.nativeElement, 'focus');
matSelect.openedChange.next(true);
fixture.detectChanges();
await fixture.whenStable();
expect(component.selectFilterInput.nativeElement.focus).toHaveBeenCalled();
});
it('should clear search term on close', async () => {
component.onModelChange('some-search-term');
expect(component.term).toBe('some-search-term');
matSelect.openedChange.next(false);
fixture.detectChanges();
await fixture.whenStable();
expect(component.term).toBe('');
});
it('should emit event when value changes', async () => {
spyOn(component.change, 'next');
component.onModelChange('some-search-term');
expect(component.change.next).toHaveBeenCalledWith('some-search-term');
});
it('should reset value on reset() event', () => {
component.onModelChange('some-search-term');
expect(component.term).toBe('some-search-term');
component.reset();
expect(component.term).toBe('');
});
it('should reset value on Escape event', () => {
component.onModelChange('some-search-term');
expect(component.term).toBe('some-search-term');
component.selectFilterInput.nativeElement.dispatchEvent(new KeyboardEvent('keydown', {keyCode: ESCAPE} as any));
fixture.detectChanges();
expect(component.term).toBe('');
});
fixture.detectChanges();
await fixture.whenStable();
expect(component.selectFilterInput.nativeElement.focus).toHaveBeenCalled();
});
describe('testComponent', () => {
beforeEach(() => {
testFixture = TestBed.createComponent(TestComponent);
testComponent = testFixture.componentInstance;
});
it('should clear search term on close', async () => {
component.onModelChange('some-search-term');
expect(component.term).toBe('some-search-term');
afterEach(() => testFixture.destroy());
matSelect.openedChange.next(false);
it('should preserve the values for multiple search', async () => {
const userSelection = [{ id: '3', name: 'three' }];
const preSelected = [
{ id: '1', name: 'one' },
{ id: '2', name: 'two' }
];
testComponent.field.value = preSelected;
testComponent.multiple = true;
testFixture.detectChanges();
fixture.detectChanges();
await fixture.whenStable();
const dropdown: HTMLElement = testFixture.nativeElement.querySelector('.mat-select-trigger');
dropdown.click();
await testFixture.whenStable();
testFixture.detectChanges();
expect(component.term).toBe('');
});
const filter = testFixture.debugElement.query(By.css('input'));
filter.triggerEventHandler('input', { target: { value: 'three' } });
testFixture.detectChanges();
it('should emit event when value changes', async () => {
spyOn(component.change, 'next');
component.onModelChange('some-search-term');
expect(component.change.next).toHaveBeenCalledWith('some-search-term');
});
const option = testFixture.debugElement.query(By.css('mat-option'));
option.triggerEventHandler('click', null);
testFixture.detectChanges();
it('should reset value on reset() event', () => {
component.onModelChange('some-search-term');
expect(component.term).toBe('some-search-term');
expect(testComponent.field.value).toEqual([...preSelected, ...userSelection]);
});
component.reset();
expect(component.term).toBe('');
});
it('should reset value on Escape event', () => {
component.onModelChange('some-search-term');
expect(component.term).toBe('some-search-term');
component.selectFilterInput.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { code: 'Escape' } as any));
fixture.detectChanges();
expect(component.term).toBe('');
});
});

View File

@@ -16,7 +16,6 @@
*/
import { Component, ViewEncapsulation, ViewChild, ElementRef, OnDestroy, Inject, Output, EventEmitter, OnInit } from '@angular/core';
import { ESCAPE, TAB } from '@angular/cdk/keycodes';
import { MatSelect } from '@angular/material/select';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@@ -43,46 +42,40 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.change
.pipe(takeUntil(this.onDestroy$))
.subscribe((val: string) => this.term = val );
this.change.pipe(takeUntil(this.onDestroy$)).subscribe((val: string) => (this.term = val));
this.matSelect.openedChange
.pipe(takeUntil(this.onDestroy$))
.subscribe((isOpened: boolean) => {
if (isOpened) {
this.selectFilterInput.nativeElement.focus();
} else {
this.change.next('');
}
});
this.matSelect.openedChange.pipe(takeUntil(this.onDestroy$)).subscribe((isOpened: boolean) => {
if (isOpened) {
this.selectFilterInput.nativeElement.focus();
} else {
this.change.next('');
}
});
if (this.matSelect.ngControl) {
this.previousSelected = this.matSelect.ngControl.value;
this.matSelect.ngControl.valueChanges
.pipe(takeUntil(this.onDestroy$))
.subscribe((values) => {
let restoreSelection = false;
if (this.matSelect.multiple && Array.isArray(this.previousSelected)) {
if (!Array.isArray(values)) {
values = [];
this.matSelect.ngControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((values) => {
let restoreSelection = false;
if (this.matSelect.multiple && Array.isArray(this.previousSelected)) {
if (!Array.isArray(values)) {
values = [];
}
const options = this.matSelect.options.map((option) => option.value);
this.previousSelected.forEach((previous) => {
const isSelected = [...values, ...options].some((current) => this.matSelect.compareWith(current, previous));
if (!isSelected) {
values.push(previous);
restoreSelection = true;
}
const options = this.matSelect.options.map(option => option.value);
this.previousSelected.forEach((previous) => {
const isSelected = [...values, ...options].some(current => this.matSelect.compareWith(current, previous));
if (!isSelected) {
values.push(previous);
restoreSelection = true;
}
});
}
});
}
this.previousSelected = values;
if (restoreSelection) {
// eslint-disable-next-line no-underscore-dangle
this.matSelect._onChange(values);
}
});
this.previousSelected = values;
if (restoreSelection) {
// eslint-disable-next-line no-underscore-dangle
this.matSelect._onChange(values);
}
});
}
}
@@ -97,13 +90,13 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy {
handleKeydown($event: KeyboardEvent) {
if (this.term) {
if ($event.keyCode === ESCAPE) {
event.stopPropagation();
if ($event.code === 'Escape') {
$event.stopPropagation();
this.change.next('');
}
if (($event.target as HTMLInputElement).tagName === 'INPUT' && $event.keyCode === TAB) {
event.stopPropagation();
if (($event.target as HTMLInputElement).tagName === 'INPUT' && $event.code === 'Tab') {
$event.stopPropagation();
}
}
}

View File

@@ -15,25 +15,24 @@
* limitations under the License.
*/
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { OverlayModule } from '@angular/cdk/overlay';
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatSnackBar, MatSnackBarConfig, MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSnackBarConfig, MatSnackBarModule } from '@angular/material/snack-bar';
import { NotificationService } from './notification.service';
import { TranslationService } from '../../translation/translation.service';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatIconHarness } from '@angular/material/icon/testing';
import { MatSnackBarHarness } from '@angular/material/snack-bar/testing';
@Component({
template: '',
providers: [NotificationService]
})
class ProvidesNotificationServiceComponent {
constructor(public notificationService: NotificationService) {
}
constructor(public notificationService: NotificationService) {}
sendMessageWithoutConfig() {
return this.notificationService.openSnackMessage('Test notification', 1000);
@@ -44,7 +43,7 @@ class ProvidesNotificationServiceComponent {
}
sendMessageWithArgs() {
return this.notificationService.openSnackMessage('Test notification {{ arg }}', 1000, {arg: 'arg'});
return this.notificationService.openSnackMessage('Test notification {{ arg }}', 1000, { arg: 'arg' });
}
sendCustomMessage() {
@@ -72,7 +71,7 @@ class ProvidesNotificationServiceComponent {
sendMessageWithDecorativeIcon() {
const notificationConfig = new MatSnackBarConfig();
notificationConfig.duration = 1000;
notificationConfig.data = {decorativeIcon: 'info'};
notificationConfig.data = { decorativeIcon: 'info' };
return this.notificationService.openSnackMessage('with decorative icon', notificationConfig);
}
@@ -84,154 +83,97 @@ class ProvidesNotificationServiceComponent {
return this.notificationService.openSnackMessageAction('with decorative icon', 'TestWarn', notificationConfig);
}
}
describe('NotificationService', () => {
let loader: HarnessLoader;
let fixture: ComponentFixture<ProvidesNotificationServiceComponent>;
let translationService: TranslationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
OverlayModule,
MatSnackBarModule
],
declarations: [ProvidesNotificationServiceComponent],
providers: [
MatSnackBar,
LiveAnnouncer
]
imports: [TranslateModule.forRoot(), CoreTestingModule, MatSnackBarModule],
declarations: [ProvidesNotificationServiceComponent]
});
translationService = TestBed.inject(TranslationService);
fixture = TestBed.createComponent(ProvidesNotificationServiceComponent);
fixture.detectChanges();
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
});
it('should translate messages', (done) => {
it('should translate messages', () => {
spyOn(translationService, 'instant').and.callThrough();
const promise = fixture.componentInstance.sendMessage();
promise.afterDismissed().subscribe(() => {
expect(translationService.instant).toHaveBeenCalled();
done();
});
fixture.componentInstance.sendMessage();
fixture.detectChanges();
expect(translationService.instant).toHaveBeenCalled();
});
it('should translate messages with args', (done) => {
it('should translate messages with args', () => {
spyOn(translationService, 'instant').and.callThrough();
const promise = fixture.componentInstance.sendMessageWithArgs();
promise.afterDismissed().subscribe(() => {
expect(translationService.instant).toHaveBeenCalledWith('Test notification {{ arg }}', {arg: 'arg'});
done();
});
fixture.componentInstance.sendMessageWithArgs();
fixture.detectChanges();
expect(translationService.instant).toHaveBeenCalledWith('Test notification {{ arg }}', { arg: 'arg' });
});
it('should translate the action', (done) => {
it('should translate the action', () => {
spyOn(translationService, 'instant').and.callThrough();
const promise = fixture.componentInstance.sendMessageAction();
promise.afterDismissed().subscribe(() => {
expect(translationService.instant).toHaveBeenCalledTimes(2);
done();
});
fixture.componentInstance.sendMessageAction();
fixture.detectChanges();
expect(translationService.instant).toHaveBeenCalledTimes(2);
});
it('should open a message notification bar', (done) => {
const promise = fixture.componentInstance.sendMessage();
promise.afterDismissed().subscribe(() => {
done();
});
it('should open a message notification bar', async () => {
fixture.componentInstance.sendMessage();
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
expect(await loader.hasHarness(MatSnackBarHarness)).toBe(true);
});
it('should open a message notification bar without custom configuration', (done) => {
const promise = fixture.componentInstance.sendMessageWithoutConfig();
promise.afterDismissed().subscribe(() => {
done();
});
it('should open a message notification bar without custom configuration', async () => {
fixture.componentInstance.sendMessageWithoutConfig();
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
expect(await loader.hasHarness(MatSnackBarHarness)).toBe(true);
});
it('should open a message notification bar with custom configuration', (done) => {
const promise = fixture.componentInstance.sendCustomMessage();
promise.afterDismissed().subscribe(() => {
done();
});
it('should open a message notification bar with custom configuration', async () => {
fixture.componentInstance.sendCustomMessage();
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
expect(await loader.hasHarness(MatSnackBarHarness)).toBe(true);
});
it('should open a message notification bar with action', (done) => {
const promise = fixture.componentInstance.sendMessageAction();
promise.afterDismissed().subscribe(() => {
done();
});
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
it('should open a message notification bar with action', async () => {
fixture.componentInstance.sendMessageAction();
expect(await loader.hasHarness(MatSnackBarHarness)).toBe(true);
});
it('should open a message notification bar with action and custom configuration', (done) => {
const promise = fixture.componentInstance.sendCustomMessageAction();
promise.afterDismissed().subscribe(() => {
done();
});
it('should open a message notification bar with action and custom configuration', async () => {
fixture.componentInstance.sendCustomMessageAction();
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
expect(await loader.hasHarness(MatSnackBarHarness)).toBe(true);
});
it('should open a message notification bar with action and no custom configuration', (done) => {
const promise = fixture.componentInstance.sendMessageActionWithoutConfig();
promise.afterDismissed().subscribe(() => {
done();
});
it('should open a message notification bar with action and no custom configuration', async () => {
fixture.componentInstance.sendMessageActionWithoutConfig();
fixture.detectChanges();
expect(document.querySelector('snack-bar-container')).not.toBeNull();
expect(await loader.hasHarness(MatSnackBarHarness)).toBe(true);
});
it('should open a message notification bar with a decorative icon', (done) => {
const promise = fixture.componentInstance.sendMessageWithDecorativeIcon();
promise.afterDismissed().subscribe(() => {
done();
});
fixture.detectChanges();
expect(document.querySelector('[data-automation-id="adf-snackbar-message-content"] mat-icon')).not.toBeNull();
it('should open a message notification bar with a decorative icon', async () => {
fixture.componentInstance.sendMessageWithDecorativeIcon();
expect(await loader.hasHarness(MatIconHarness.with({ ancestor: `[data-automation-id="adf-snackbar-message-content"]` }))).toBe(true);
});
it('should open a message notification bar with action and a decorative icon', (done) => {
const promise = fixture.componentInstance.sendMessageWithDecorativeIconAndAction();
promise.afterDismissed().subscribe(() => {
done();
});
fixture.detectChanges();
expect(document.querySelector('[data-automation-id="adf-snackbar-message-content"] mat-icon')).not.toBeNull();
});
it('should open a message notification bar with action and a decorative icon', async () => {
fixture.componentInstance.sendMessageWithDecorativeIconAndAction();
expect(await loader.hasHarness(MatIconHarness.with({ ancestor: `[data-automation-id="adf-snackbar-message-content"]` }))).toBe(true);
});
});

View File

@@ -24,33 +24,32 @@ import { DEFAULT_APP_INSTANCE_THEME } from '../models/application-instance.model
import { TranslateModule } from '@ngx-translate/core';
describe('AppDetailsCloudComponent', () => {
let component: AppDetailsCloudComponent;
let fixture: ComponentFixture<AppDetailsCloudComponent>;
let host: HTMLElement;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule,
AppListCloudModule
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule, AppListCloudModule]
});
fixture = TestBed.createComponent(AppDetailsCloudComponent);
component = fixture.componentInstance;
host = fixture.nativeElement as HTMLElement;
component.applicationInstance = fakeApplicationInstance[0];
});
const getAppCard = () => host.querySelector<HTMLElement>('.adf-app-listgrid-item-card');
it('should display application name', () => {
fixture.detectChanges();
const appName = fixture.nativeElement.querySelector('.adf-app-listgrid-item-card-title');
const appName = host.querySelector<HTMLDivElement>('.adf-app-listgrid-item-card-title');
expect(appName.innerText.trim()).toEqual(fakeApplicationInstance[0].name);
});
it('should emit a click event when app selected', () => {
spyOn(component.selectedApp, 'emit');
fixture.detectChanges();
const app = fixture.nativeElement.querySelector('.mat-card');
const app = getAppCard();
app.click();
expect(component.selectedApp.emit).toHaveBeenCalledWith(fakeApplicationInstance[0]);
});
@@ -59,17 +58,17 @@ describe('AppDetailsCloudComponent', () => {
component.applicationInstance = fakeApplicationInstance[2];
fixture.detectChanges();
const theme = fixture.nativeElement.querySelector('.adf-app-listgrid-item-card').getAttribute('ng-reflect-ng-class');
const icon = fixture.nativeElement.querySelector('.adf-app-listgrid-item-card-logo-icon');
const card = getAppCard();
expect(card.classList.contains(DEFAULT_APP_INSTANCE_THEME));
expect(theme).toEqual(DEFAULT_APP_INSTANCE_THEME);
const icon = host.querySelector('.adf-app-listgrid-item-card-logo-icon');
expect(icon).toBeTruthy();
});
it('should render card with a non ApplicationInstanceModel input object', () => {
component.applicationInstance = { name: 'application-new-3', createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending' };
component.applicationInstance = { name: 'application-new-3', createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending' };
fixture.detectChanges();
const app = fixture.nativeElement.querySelector('.mat-card');
const app = getAppCard();
expect(app).toBeTruthy();
});
});
});

View File

@@ -15,7 +15,7 @@
<mat-list-item class="adf-app-list-item" (click)="onSelectApp(app)" (keyup.enter)="onSelectApp(app)"
*ngFor="let app of appsList" tabindex="0" role="button" title="{{app.name}}">
<mat-icon matListIcon>touch_app</mat-icon>
<span matLine>{{app.name}}</span>
<span class="adf-app-list-item-text" matLine>{{app.name}}</span>
</mat-list-item>
</mat-list>
</ng-template>

View File

@@ -27,7 +27,6 @@ import { ProcessServiceCloudTestingModule } from '../../testing/process-service-
import { TranslateModule } from '@ngx-translate/core';
describe('AppListCloudComponent', () => {
let component: AppListCloudComponent;
let fixture: ComponentFixture<AppListCloudComponent>;
let appsProcessCloudService: AppsProcessCloudService;
@@ -35,32 +34,28 @@ describe('AppListCloudComponent', () => {
let alfrescoApiService: AlfrescoApiService;
const mock: any = {
oauth2Auth: {
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
},
isEcmLoggedIn: () => false,
reply: jasmine.createSpy('reply')
oauth2Auth: {
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
},
isEcmLoggedIn: () => false,
reply: jasmine.createSpy('reply')
};
@Component({
template: `
<adf-cloud-app-list>
<adf-custom-empty-content-template>
<mat-icon>apps</mat-icon>
<p id="custom-id">No Apps Found</p>
</adf-custom-empty-content-template>
</adf-cloud-app-list>
`
<adf-cloud-app-list>
<adf-custom-empty-content-template>
<mat-icon>apps</mat-icon>
<p id="custom-id">No Apps Found</p>
</adf-custom-empty-content-template>
</adf-cloud-app-list>
`
})
class CustomEmptyAppListCloudTemplateComponent {
}
class CustomEmptyAppListCloudTemplateComponent {}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
],
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule],
declarations: [CustomEmptyAppListCloudTemplateComponent]
});
fixture = TestBed.createComponent(AppListCloudComponent);
@@ -127,10 +122,9 @@ describe('AppListCloudComponent', () => {
expect(getAppsSpy).toHaveBeenCalled();
done();
});
});
});
describe('Grid Layout ', () => {
it('should display a grid by default', () => {
fixture.detectChanges();
expect(component.isGrid()).toBe(true);
@@ -165,7 +159,6 @@ describe('AppListCloudComponent', () => {
});
describe('List Layout ', () => {
beforeEach(() => {
component.layoutType = LAYOUT_LIST;
});
@@ -176,11 +169,11 @@ describe('AppListCloudComponent', () => {
expect(component.isList()).toBe(true);
});
it('should defined mat-list when layout type is LIST', () => {
it('should display list when layout type is LIST', () => {
fixture.detectChanges();
const appListElement = fixture.nativeElement.querySelectorAll('mat-list');
const appListItemElement = fixture.nativeElement.querySelectorAll('mat-list-item');
const appName = fixture.nativeElement.querySelector('.mat-list-text');
const appListElement = fixture.nativeElement.querySelectorAll('.adf-app-list');
const appListItemElement = fixture.nativeElement.querySelectorAll('.adf-app-list-item');
const appName = fixture.nativeElement.querySelector('.adf-app-list-item-text');
expect(appListElement).toBeDefined();
expect(appListElement).not.toBeNull();
@@ -200,7 +193,7 @@ describe('AppListCloudComponent', () => {
it('should emit a click event when app selected', () => {
spyOn(component.appClick, 'emit');
fixture.detectChanges();
const onAppClick = fixture.nativeElement.querySelector('.mat-card');
const onAppClick = fixture.nativeElement.querySelector('.adf-app-listgrid-item-card');
onAppClick.click();
expect(component.appClick.emit).toHaveBeenCalledWith(fakeApplicationInstance[0]);
});
@@ -221,7 +214,7 @@ describe('AppListCloudComponent', () => {
customFixture.detectChanges();
await customFixture.whenStable();
const title: any = customFixture.nativeElement.querySelector('#custom-id');
const title: any = customFixture.nativeElement.querySelector('#custom-id');
expect(title.innerText).toBe('No Apps Found');
});
});

View File

@@ -26,39 +26,29 @@ import { DebugElement, SimpleChange } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { IdentityGroupService } from '../services/identity-group.service';
import { mockFoodGroups, mockMeatChicken, mockVegetableAubergine } from '../mock/group-cloud.mock';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatChipHarness, MatChipListHarness } from '@angular/material/chips/testing';
import { MatIconHarness } from '@angular/material/icon/testing';
import { MatInputHarness } from '@angular/material/input/testing';
describe('GroupCloudComponent', () => {
let loader: HarnessLoader;
let component: GroupCloudComponent;
let fixture: ComponentFixture<GroupCloudComponent>;
let element: HTMLElement;
let identityGroupService: IdentityGroupService;
let findGroupsByNameSpy: jasmine.Spy;
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
/**
* get the native element for the selector
*
* @param selector selector
* @returns native element
*/
function getElement<T = HTMLElement>(selector: string): T {
return fixture.nativeElement.querySelector(selector);
}
/**
* search group by value
*
* @param value element input value
*/
async function searchGroup(value: string) {
const input = getElement<HTMLInputElement>('input');
input.focus();
input.value = value;
input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
await fixture.whenStable();
fixture.detectChanges();
const input = await loader.getHarness(MatInputHarness);
await input.focus();
await input.setValue(value);
}
/**
@@ -67,17 +57,10 @@ describe('GroupCloudComponent', () => {
* @param value value
*/
async function searchGroupsAndBlur(value: string) {
const input = getElement<HTMLInputElement>('input');
input.focus();
input.value = value;
input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
await fixture.whenStable();
fixture.detectChanges();
input.blur();
fixture.detectChanges();
const input = await loader.getHarness(MatInputHarness);
await input.focus();
await input.setValue(value);
await input.blur();
}
/**
@@ -91,18 +74,14 @@ describe('GroupCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
ProcessServiceCloudTestingModule,
GroupCloudModule
]
imports: [TranslateModule.forRoot(), CoreTestingModule, ProcessServiceCloudTestingModule, GroupCloudModule]
});
fixture = TestBed.createComponent(GroupCloudComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
identityGroupService = TestBed.inject(IdentityGroupService);
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should populate placeholder when title is present', () => {
@@ -115,7 +94,6 @@ describe('GroupCloudComponent', () => {
});
describe('Search group', () => {
beforeEach(() => {
fixture.detectChanges();
findGroupsByNameSpy = spyOn(identityGroupService, 'search').and.returnValue(of(mockFoodGroups));
@@ -208,19 +186,19 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges();
});
it('should not pre-select any group when preSelectGroups is empty - single mode', () => {
it('should not pre-select any group when preSelectGroups is empty - single mode', async () => {
component.mode = 'single';
fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toEqual(0);
});
it('should not pre-select any group when preSelectGroups is empty - multiple mode', () => {
it('should not pre-select any group when preSelectGroups is empty - multiple mode', async () => {
component.mode = 'multiple';
fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toEqual(0);
});
});
@@ -235,10 +213,11 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges();
});
it('should show only one mat chip with the first preSelectedGroup', () => {
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
it('should show only one mat chip with the first preSelectedGroup', async () => {
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toEqual(1);
expect(chips[0].attributes['data-automation-id']).toEqual(`adf-cloud-group-chip-${mockVegetableAubergine.name}`);
const testId = await (await chips[0].host()).getAttribute('data-automation-id');
expect(testId).toEqual(`adf-cloud-group-chip-${mockVegetableAubergine.name}`);
});
});
@@ -252,12 +231,13 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges();
});
it('should render all preselected groups', () => {
it('should render all preselected groups', async () => {
component.mode = 'multiple';
fixture.detectChanges();
component.ngOnChanges({ preSelectGroups: change });
fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
});
@@ -266,42 +246,40 @@ describe('GroupCloudComponent', () => {
const changedGroupsEmitterSpy = spyOn(component.changedGroups, 'emit');
component.mode = 'multiple';
const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon'));
removeIcon.nativeElement.click();
fixture.detectChanges();
const chip = await loader.getHarness(MatChipHarness);
const icon = await chip.getHarness(MatIconHarness);
await (await icon.host()).click();
await fixture.whenStable();
expect(removeGroupEmitterSpy).toHaveBeenCalledWith(mockVegetableAubergine);
expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockMeatChicken]);
expect(component.selectedGroups.indexOf({
id: mockMeatChicken.id,
name: mockMeatChicken.name
})).toEqual(-1);
expect(
component.selectedGroups.indexOf({
id: mockMeatChicken.id,
name: mockMeatChicken.name
})
).toEqual(-1);
});
});
describe('Multiple Mode with read-only', () => {
it('Should not show remove icon for pre-selected groups if readonly property set to true', async () => {
component.mode = 'multiple';
component.preSelectGroups = [
{ id: mockVegetableAubergine.id, name: mockVegetableAubergine.name, readonly: true },
mockMeatChicken
];
component.preSelectGroups = [{ id: mockVegetableAubergine.id, name: mockVegetableAubergine.name, readonly: true }, mockMeatChicken];
const changes = new SimpleChange(null, [{ name: mockVegetableAubergine.name }], false);
component.ngOnChanges({ preSelectGroups: changes });
fixture.detectChanges();
await fixture.whenStable();
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
expect(chipList.length).toBe(2);
const removeIconAubergine = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockVegetableAubergine.name}"]`);
const removeIconAubergine = element.querySelector(
`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockVegetableAubergine.name}"]`
);
expect(removeIconAubergine).toBeNull();
const removeIconPepper = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
const removeIconPepper = element.querySelector(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
expect(removeIconPepper).not.toBeNull();
});
it('Should be able to remove preselected groups if readonly property set to false', async () => {
@@ -314,28 +292,26 @@ describe('GroupCloudComponent', () => {
const removeGroupSpy = spyOn(component.removeGroup, 'emit');
fixture.detectChanges();
fixture.whenStable();
fixture.detectChanges();
let chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
expect(chipList.length).toBe(2);
const removeIcon = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
const removeIcon = element.querySelector<HTMLElement>(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
removeIcon.click();
fixture.detectChanges();
expect(removeGroupSpy).toHaveBeenCalled();
expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1);
chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(1);
});
it('should removeDuplicatedGroups return only unique groups', () => {
const duplicatedGroups = [ mockMeatChicken, mockMeatChicken];
const duplicatedGroups = [mockMeatChicken, mockMeatChicken];
expect(component.removeDuplicatedGroups(duplicatedGroups)).toEqual([mockMeatChicken]);
});
});
describe('Preselected groups and validation enabled', () => {
beforeEach(() => {
spyOn(identityGroupService, 'search').and.throwError('Invalid group');
component.validate = true;
@@ -364,7 +340,7 @@ describe('GroupCloudComponent', () => {
describe('Component readonly mode', () => {
const change = new SimpleChange(null, mockFoodGroups, false);
it('should chip list be disabled and show one single chip - single mode', () => {
it('should chip list be disabled and show one single chip - single mode', async () => {
component.mode = 'single';
component.readOnly = true;
component.preSelectGroups = mockFoodGroups;
@@ -372,16 +348,14 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chipList = getElement('mat-chip-list');
expect(chips).toBeDefined();
expect(chipList).toBeDefined();
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(1);
expect(chipList.attributes['ng-reflect-disabled']?.value).toEqual('true');
const chipList = await loader.getHarness(MatChipListHarness);
expect(await chipList.isDisabled()).toBe(true);
});
it('should chip list be disabled and show all the chips - multiple mode', () => {
it('should chip list be disabled and show all the chips - multiple mode', async () => {
component.mode = 'multiple';
component.readOnly = true;
component.preSelectGroups = mockFoodGroups;
@@ -389,14 +363,11 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges();
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
const chipList = getElement('mat-chip-list');
expect(chips).toBeDefined();
expect(chipList).toBeDefined();
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
expect(chipList.attributes['ng-reflect-disabled']?.value).toEqual('true');
const chipList = await loader.getHarness(MatChipListHarness);
expect(await chipList.isDisabled()).toBe(true);
});
});
});

View File

@@ -26,46 +26,29 @@ import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token';
import {
mockFoodUsers,
mockKielbasaSausage,
mockShepherdsPie,
mockYorkshirePudding,
mockPreselectedFoodUsers
} from '../mock/people-cloud.mock';
import { mockFoodUsers, mockKielbasaSausage, mockShepherdsPie, mockYorkshirePudding, mockPreselectedFoodUsers } from '../mock/people-cloud.mock';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatChipHarness, MatChipListHarness } from '@angular/material/chips/testing';
import { MatInputHarness } from '@angular/material/input/testing';
describe('PeopleCloudComponent', () => {
let loader: HarnessLoader;
let component: PeopleCloudComponent;
let fixture: ComponentFixture<PeopleCloudComponent>;
let element: HTMLElement;
let identityUserService: IdentityUserServiceInterface;
let searchSpy: jasmine.Spy;
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
/**
* get the native element by selector
*
* @param selector selector
* @returns native element
*/
function getElement<T = HTMLElement>(selector: string): T {
return fixture.nativeElement.querySelector(selector);
}
/**
* Search users by value
*
* @param value value
*/
async function searchUsers(value: string) {
const input = getElement<HTMLInputElement>('input');
input.focus();
input.value = value;
input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
await fixture.whenStable();
fixture.detectChanges();
const input = await loader.getHarness(MatInputHarness);
await input.focus();
await input.setValue(value);
}
/**
@@ -74,17 +57,10 @@ describe('PeopleCloudComponent', () => {
* @param value value
*/
async function searchUsersAndBlur(value: string) {
const input = getElement<HTMLInputElement>('input');
input.focus();
input.value = value;
input.dispatchEvent(new Event('keyup'));
input.dispatchEvent(new Event('input'));
await fixture.whenStable();
fixture.detectChanges();
input.blur();
fixture.detectChanges();
const input = await loader.getHarness(MatInputHarness);
await input.focus();
await input.setValue(value);
await input.blur();
}
/**
@@ -96,15 +72,6 @@ describe('PeopleCloudComponent', () => {
return fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]'));
}
/**
* Get users chip list UI
*
* @returns list of debug elements
*/
function getUsersChipsUI(): DebugElement[] {
return fixture.debugElement.queryAll(By.css('mat-chip'));
}
/**
* Get the first user from the list
*
@@ -116,32 +83,29 @@ describe('PeopleCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
ProcessServiceCloudTestingModule,
PeopleCloudModule
]
imports: [TranslateModule.forRoot(), CoreTestingModule, ProcessServiceCloudTestingModule, PeopleCloudModule]
});
fixture = TestBed.createComponent(PeopleCloudComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
identityUserService = TestBed.inject(IDENTITY_USER_SERVICE_TOKEN);
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should populate placeholder when title is present', () => {
component.title = 'TITLE_KEY';
fixture.detectChanges();
const matLabel = getElement<HTMLInputElement>('#adf-people-cloud-title-id');
const matLabel = element.querySelector<HTMLInputElement>('#adf-people-cloud-title-id');
expect(matLabel.textContent).toEqual('TITLE_KEY');
});
it('should not populate placeholder when title is not present', () => {
it('should not populate placeholder when title is not present', () => {
fixture.detectChanges();
const matLabel = getElement<HTMLInputElement>('#adf-people-cloud-title-id');
const matLabel = element.querySelector<HTMLInputElement>('#adf-people-cloud-title-id');
expect(matLabel.textContent).toEqual('');
});
@@ -196,7 +160,9 @@ describe('PeopleCloudComponent', () => {
});
it('should not be able to search for a user that his email matches one of the excluded users email', async () => {
component.excludedUsers = [{ email: mockKielbasaSausage.email, username: 'new-username', firstName: 'new-first-name', lastName: 'new-last-name' }];
component.excludedUsers = [
{ email: mockKielbasaSausage.email, username: 'new-username', firstName: 'new-first-name', lastName: 'new-last-name' }
];
fixture.detectChanges();
await searchUsers('first-name');
@@ -205,7 +171,15 @@ describe('PeopleCloudComponent', () => {
});
it('should not be able to search for a user that his id matches one of the excluded users id', async () => {
component.excludedUsers = [{ id: mockKielbasaSausage.id, username: 'new-username', firstName: 'new-first-name', lastName: 'new-last-name', email: 'new-email@food.com' }];
component.excludedUsers = [
{
id: mockKielbasaSausage.id,
username: 'new-username',
firstName: 'new-first-name',
lastName: 'new-last-name',
email: 'new-email@food.com'
}
];
fixture.detectChanges();
await searchUsers('first-name');
@@ -214,7 +188,9 @@ describe('PeopleCloudComponent', () => {
});
it('should not be able to search for a user that his username matches one of the excluded users username', async () => {
component.excludedUsers = [{ username: mockKielbasaSausage.username, firstName: 'new-first-name', lastName: 'new-last-name', email: 'new-email@food.com' }];
component.excludedUsers = [
{ username: mockKielbasaSausage.username, firstName: 'new-first-name', lastName: 'new-last-name', email: 'new-email@food.com' }
];
fixture.detectChanges();
await searchUsers('first-name');
@@ -301,17 +277,20 @@ describe('PeopleCloudComponent', () => {
});
describe('No preselected users', () => {
it('should not pre-select any user when preSelectUsers is empty - single mode', () => {
it('should not pre-select any user when preSelectUsers is empty - single mode', async () => {
component.mode = 'single';
fixture.detectChanges();
expect(getUsersChipsUI().length).toEqual(0);
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(0);
});
it('should not pre-select any users when preSelectUsers is empty - multiple mode', () => {
it('should not pre-select any users when preSelectUsers is empty - multiple mode', async () => {
component.mode = 'multiple';
fixture.detectChanges();
expect(getUsersChipsUI().length).toEqual(0);
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toEqual(0);
});
});
@@ -327,14 +306,16 @@ describe('PeopleCloudComponent', () => {
element = fixture.nativeElement;
});
it('should show only one mat chip with the first preSelectedUser', () => {
expect(getUsersChipsUI().length).toEqual(1);
expect(getUsersChipsUI()[0].attributes['data-automation-id']).toEqual(`adf-people-cloud-chip-${mockPreselectedFoodUsers[0].username}`);
it('should show only one mat chip with the first preSelectedUser', async () => {
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toEqual(1);
const testId = await (await chips[0].host()).getAttribute('data-automation-id');
expect(testId).toEqual(`adf-people-cloud-chip-${mockPreselectedFoodUsers[0].username}`);
});
});
describe('Multiple Mode with Pre-selected Users', () => {
beforeEach(() => {
component.mode = 'multiple';
});
@@ -347,9 +328,15 @@ describe('PeopleCloudComponent', () => {
await fixture.whenStable();
fixture.detectChanges();
expect(getUsersChipsUI().length).toEqual(2);
expect(getUsersChipsUI()[0].attributes['data-automation-id']).toEqual(`adf-people-cloud-chip-${mockPreselectedFoodUsers[0].username}`);
expect(getUsersChipsUI()[1].attributes['data-automation-id']).toEqual(`adf-people-cloud-chip-${mockPreselectedFoodUsers[1].username}`);
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toEqual(2);
const testId1 = await (await chips[0].host()).getAttribute('data-automation-id');
const testId2 = await (await chips[1].host()).getAttribute('data-automation-id');
expect(testId1).toEqual(`adf-people-cloud-chip-${mockPreselectedFoodUsers[0].username}`);
expect(testId2).toEqual(`adf-people-cloud-chip-${mockPreselectedFoodUsers[1].username}`);
});
it('Should not show remove icon for pre-selected users if readonly property set to true', async () => {
@@ -361,12 +348,16 @@ describe('PeopleCloudComponent', () => {
const change = new SimpleChange(null, component.preSelectUsers, false);
component.ngOnChanges({ preSelectUsers: change });
await fixture.whenStable();
fixture.detectChanges();
await fixture.whenStable();
const removeIcon = getElement(`[data-automation-id="adf-people-cloud-chip-remove-icon-${mockPreselectedFoodUsers[0].username}"]`);
const removeIcon = element.querySelector(
`[data-automation-id="adf-people-cloud-chip-remove-icon-${mockPreselectedFoodUsers[0].username}"]`
);
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
expect(getUsersChipsUI().length).toBe(2);
expect(component.preSelectUsers[0].readonly).toBeTruthy();
expect(component.preSelectUsers[1].readonly).toBeTruthy();
expect(removeIcon).toBeNull();
@@ -380,27 +371,32 @@ describe('PeopleCloudComponent', () => {
const removeUserSpy = spyOn(component.removeUser, 'emit');
await fixture.whenStable();
fixture.detectChanges();
await fixture.whenStable();
const removeIcon = getElement(`[data-automation-id="adf-people-cloud-chip-remove-icon-${mockPreselectedFoodUsers[0].username}"]`);
const removeIcon = element.querySelector<HTMLElement>(
`[data-automation-id="adf-people-cloud-chip-remove-icon-${mockPreselectedFoodUsers[0].username}"]`
);
expect(getUsersChipsUI().length).toBe(2);
expect(component.preSelectUsers[0].readonly).toBe(false, 'Removable');
expect(component.preSelectUsers[1].readonly).toBe(false, 'Removable');
let chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
expect(component.preSelectUsers[0].readonly).toBe(false);
expect(component.preSelectUsers[1].readonly).toBe(false);
removeIcon.click();
fixture.detectChanges();
expect(removeUserSpy).toHaveBeenCalled();
expect(getUsersChipsUI().length).toBe(1);
chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(1);
});
describe('Component readonly mode', () => {
const change = new SimpleChange(null, mockPreselectedFoodUsers, false);
it('should chip list be disabled and show one single chip - single mode', () => {
it('should chip list be disabled and show one single chip - single mode', async () => {
component.mode = 'single';
component.readOnly = true;
component.preSelectUsers = mockPreselectedFoodUsers;
@@ -408,15 +404,14 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges();
const chipList = getElement('mat-chip-list');
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(1);
expect(getUsersChipsUI()).toBeDefined();
expect(chipList).toBeDefined();
expect(getUsersChipsUI().length).toBe(1);
expect(chipList.attributes['ng-reflect-disabled'].value).toEqual('true');
const chipList = await loader.getHarness(MatChipListHarness);
expect(await chipList.isDisabled()).toBe(true);
});
it('should chip list be disabled and show mat chips for all the preselected users - multiple mode', () => {
it('should chip list be disabled and show mat chips for all the preselected users - multiple mode', async () => {
component.mode = 'multiple';
component.readOnly = true;
component.preSelectUsers = mockPreselectedFoodUsers;
@@ -424,18 +419,16 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges();
const chipList = getElement('mat-chip-list');
const chips = await loader.getAllHarnesses(MatChipHarness);
expect(chips.length).toBe(2);
expect(getUsersChipsUI()).toBeDefined();
expect(chipList).toBeDefined();
expect(getUsersChipsUI().length).toBe(2);
expect(chipList.attributes['ng-reflect-disabled'].value).toEqual('true');
const chipList = await loader.getHarness(MatChipListHarness);
expect(await chipList.isDisabled()).toBe(true);
});
});
});
describe('Preselected users and validation enabled', () => {
beforeEach(() => {
spyOn(identityUserService, 'search').and.throwError('Invalid user');
component.validate = true;

View File

@@ -44,6 +44,9 @@ import { HttpClientModule } from '@angular/common/http';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { PreferenceCloudServiceInterface } from '@alfresco/adf-process-services-cloud';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
@Component({
template: ` <adf-cloud-process-list #processListCloud>
@@ -68,6 +71,7 @@ class CustomTaskListComponent {
}
describe('ProcessListCloudComponent', () => {
let loader: HarnessLoader;
let component: ProcessListCloudComponent;
let fixture: ComponentFixture<ProcessListCloudComponent>;
let appConfig: AppConfigService;
@@ -118,6 +122,7 @@ describe('ProcessListCloudComponent', () => {
});
component.isColumnSchemaCreated$ = of(true).pipe(shareReplay(1));
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -132,41 +137,39 @@ describe('ProcessListCloudComponent', () => {
expect(component.columns.length).toEqual(10);
});
it('should display empty content when process list is empty', () => {
it('should display empty content when process list is empty', async () => {
const emptyList = { list: { entries: [] } };
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList));
fixture.detectChanges();
expect(component.isLoading).toBe(true);
let loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent.nativeElement).toBeDefined();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngOnChanges({ appName });
fixture.detectChanges();
loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent).toBeFalsy();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent.nativeElement).toBeDefined();
});
it('should load spinner and show the content', () => {
it('should load spinner and show the content', async () => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
fixture.detectChanges();
expect(component.isLoading).toBe(true);
let loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent.nativeElement).toBeDefined();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
component.ngOnChanges({ appName });
fixture.detectChanges();
expect(component.isLoading).toBe(false);
loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent).toBeFalsy();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent).toBeFalsy();

View File

@@ -21,7 +21,6 @@
data-automation-id="adf-select-cloud-process-dropdown">
<mat-label>{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.LABEL.TYPE' | translate }}</mat-label>
<input
#inputAutocomplete
matInput
formControlName="processDefinition"
[matAutocomplete]="auto"

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { SimpleChange, DebugElement } from '@angular/core';
import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { FormModel } from '@alfresco/adf-core';
import { of, throwError } from 'rxjs';
@@ -30,7 +30,6 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
fakeProcessDefinitions,
fakeStartForm,
@@ -50,8 +49,13 @@ import { ProcessInstanceCloud } from '../models/process-instance-cloud.model';
import { ESCAPE } from '@angular/cdk/keycodes';
import { ProcessDefinitionCloud, TaskVariableCloud } from '@alfresco/adf-process-services-cloud';
import { first } from 'rxjs/operators';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatAutocompleteHarness } from '@angular/material/autocomplete/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
describe('StartProcessCloudComponent', () => {
let loader: HarnessLoader;
let component: StartProcessCloudComponent;
let fixture: ComponentFixture<StartProcessCloudComponent>;
let processService: StartProcessCloudService;
@@ -64,18 +68,11 @@ describe('StartProcessCloudComponent', () => {
const firstChange = new SimpleChange(undefined, 'myApp', true);
const selectOptionByName = async (name: string) => {
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-autocomplete-panel .mat-option'));
const currentOption: DebugElement = options.find(
(option: DebugElement) => option.nativeElement.querySelector('.mat-option-text').innerHTML.trim() === name
);
const arrowButton = await loader.getHarness(MatButtonHarness.with({ selector: '#adf-select-process-dropdown' }));
await arrowButton.click();
if (currentOption) {
currentOption.nativeElement.click();
}
const panel = await loader.getHarness(MatAutocompleteHarness);
await panel.selectOption({ text: name });
};
const typeValueInto = (selector: any, value: string) => {
@@ -112,6 +109,7 @@ describe('StartProcessCloudComponent', () => {
spyOn(processService, 'updateProcess').and.returnValue(of());
startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(of(fakeProcessInstance));
getStartEventFormStaticValuesMappingSpy = spyOn(processService, 'getStartEventFormStaticValuesMapping').and.returnValue(of([]));
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -372,27 +370,12 @@ describe('StartProcessCloudComponent', () => {
});
it('should display the correct number of processes in the select list', async () => {
await fixture.whenStable();
const arrowButton = await loader.getHarness(MatButtonHarness.with({ selector: '#adf-select-process-dropdown' }));
await arrowButton.click();
const arrowButton = fixture.nativeElement.querySelector('button#adf-select-process-dropdown');
arrowButton.click();
fixture.detectChanges();
const processLists = fixture.debugElement.query(By.css('.mat-autocomplete-panel'));
expect(processLists.children.length).toBe(4);
});
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();
const panel = await loader.getHarness(MatAutocompleteHarness);
const options = await panel.getOptions();
expect(options.length).toBe(4);
});
it('should display the key when the processDefinition name is empty or null', async () => {
@@ -400,14 +383,12 @@ describe('StartProcessCloudComponent', () => {
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[0].nativeElement.textContent.trim()).toBe('NewProcess 1');
const arrowButton = await loader.getHarness(MatButtonHarness.with({ selector: '#adf-select-process-dropdown' }));
await arrowButton.click();
const panel = await loader.getHarness(MatAutocompleteHarness);
const options = await panel.getOptions();
expect(await options[0].getText()).toBe('NewProcess 1');
});
it('should indicate an error to the user if process defs cannot be loaded', async () => {
@@ -486,7 +467,7 @@ describe('StartProcessCloudComponent', () => {
component.processDefinitionName = 'process';
component.appName = 'myApp';
component.ngOnChanges({});
selectOptionByName('process');
await selectOptionByName('process');
fixture.detectChanges();
await fixture.whenStable();
@@ -550,20 +531,6 @@ describe('StartProcessCloudComponent', () => {
fixture.detectChanges();
});
it('should have floating labels for process name and type', async () => {
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');
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 });
@@ -837,7 +804,7 @@ describe('StartProcessCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
component.processDefinitionName = 'processwithoutform1';
selectOptionByName(fakeProcessDefinitions[0].name);
await selectOptionByName(fakeProcessDefinitions[0].name);
fixture.detectChanges();
await fixture.whenStable();
expect(emitSpy).toHaveBeenCalledOnceWith(fakeProcessDefinitions[0]);

View File

@@ -33,6 +33,9 @@ import {
import { TaskCloudService } from '../../services/task-cloud.service';
import { TranslateModule } from '@ngx-translate/core';
import { IdentityUserService } from '../../../people/services/identity-user.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
const taskDetails: TaskDetailsCloudModel = {
appName: 'simple-app',
@@ -50,7 +53,7 @@ const taskDetails: TaskDetailsCloudModel = {
};
describe('TaskFormCloudComponent', () => {
let loader: HarnessLoader;
let taskCloudService: TaskCloudService;
let identityUserService: IdentityUserService;
@@ -63,10 +66,7 @@ describe('TaskFormCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule]
});
taskDetails.status = TASK_ASSIGNED_STATE;
taskDetails.permissions = [TASK_VIEW_PERMISSION];
@@ -82,6 +82,7 @@ describe('TaskFormCloudComponent', () => {
fixture = TestBed.createComponent(TaskFormCloudComponent);
debugElement = fixture.debugElement;
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -89,7 +90,6 @@ describe('TaskFormCloudComponent', () => {
});
describe('Complete button', () => {
beforeEach(() => {
component.taskId = 'task1';
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
@@ -120,7 +120,6 @@ describe('TaskFormCloudComponent', () => {
});
describe('Claim/Unclaim buttons', () => {
beforeEach(() => {
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
getTaskSpy.and.returnValue(of(taskDetails));
@@ -213,7 +212,6 @@ describe('TaskFormCloudComponent', () => {
});
describe('Cancel button', () => {
it('should show cancel button by default', () => {
component.appName = 'app1';
component.taskId = 'task1';
@@ -238,7 +236,6 @@ describe('TaskFormCloudComponent', () => {
});
describe('Inputs', () => {
it('should not show complete/claim/unclaim buttons when readOnly=true', () => {
component.appName = 'app1';
component.taskId = 'task1';
@@ -284,7 +281,6 @@ describe('TaskFormCloudComponent', () => {
});
describe('Events', () => {
beforeEach(() => {
component.appName = 'app1';
component.taskId = 'task1';
@@ -292,7 +288,7 @@ describe('TaskFormCloudComponent', () => {
});
it('should emit cancelClick when cancel button is clicked', async () => {
spyOn(component.cancelClick,'emit').and.stub();
spyOn(component.cancelClick, 'emit').and.stub();
fixture.detectChanges();
@@ -395,22 +391,18 @@ describe('TaskFormCloudComponent', () => {
expect(reloadSpy).toHaveBeenCalled();
});
it('should show loading template while task data is being loaded', () => {
it('should show loading template while task data is being loaded', async () => {
component.loading = true;
fixture.detectChanges();
const loadingTemplate = debugElement.query(By.css('mat-progress-spinner'));
expect(loadingTemplate).toBeDefined();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
});
it('should not show loading template while task data is not being loaded', () => {
it('should not show loading template while task data is not being loaded', async () => {
component.loading = false;
fixture.detectChanges();
const loadingTemplate = debugElement.query(By.css('mat-progress-spinner'));
expect(loadingTemplate).toBeNull();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
});
it('should emit an executeOutcome event when form outcome executed', () => {

View File

@@ -27,12 +27,19 @@ import { TranslateModule } from '@ngx-translate/core';
import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.model';
import { shareReplay, skip } from 'rxjs/operators';
import { ServiceTaskListCloudService } from '../services/service-task-list-cloud.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
@Component({
template: `
<adf-cloud-service-task-list #taskListCloud>
template: ` <adf-cloud-service-task-list #taskListCloud>
<data-columns>
<data-column key="activityName" title="ADF_CLOUD_TASK_LIST.PROPERTIES.NAME" class="adf-full-width adf-name-column" [order]="2"></data-column>
<data-column
key="activityName"
title="ADF_CLOUD_TASK_LIST.PROPERTIES.NAME"
class="adf-full-width adf-name-column"
[order]="2"
></data-column>
<data-column key="startedDate" title="ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED" class="adf-hidden"></data-column>
</data-columns>
</adf-cloud-service-task-list>`
@@ -43,18 +50,16 @@ class CustomTaskListComponent {
}
@Component({
template: `
<adf-cloud-service-task-list>
<adf-custom-empty-content-template>
<p id="custom-id"></p>
</adf-custom-empty-content-template>
</adf-cloud-service-task-list>
`
<adf-cloud-service-task-list>
<adf-custom-empty-content-template>
<p id="custom-id"></p>
</adf-custom-empty-content-template>
</adf-cloud-service-task-list>
`
})
class EmptyTemplateComponent {
}
class EmptyTemplateComponent {}
@Component({
template: `
<adf-cloud-service-task-list>
template: ` <adf-cloud-service-task-list>
<data-columns>
<data-column [copyContent]="true" key="id" title="ADF_CLOUD_TASK_LIST.PROPERTIES.ID"></data-column>
<data-column key="activityName" title="ADF_CLOUD_TASK_LIST.PROPERTIES.NAME"></data-column>
@@ -67,6 +72,7 @@ class CustomCopyContentTaskListComponent {
}
describe('ServiceTaskListCloudComponent', () => {
let loader: HarnessLoader;
let component: ServiceTaskListCloudComponent;
let fixture: ComponentFixture<ServiceTaskListCloudComponent>;
let appConfig: AppConfigService;
@@ -74,13 +80,8 @@ describe('ServiceTaskListCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
],
declarations: [
EmptyTemplateComponent
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule],
declarations: [EmptyTemplateComponent]
});
appConfig = TestBed.inject(AppConfigService);
serviceTaskListCloudService = TestBed.inject(ServiceTaskListCloudService);
@@ -108,6 +109,7 @@ describe('ServiceTaskListCloudComponent', () => {
});
component.isColumnSchemaCreated$ = of(true).pipe(shareReplay(1));
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -120,19 +122,18 @@ describe('ServiceTaskListCloudComponent', () => {
expect(component.columns.length).toEqual(3);
});
it('should display empty content when process list is empty', () => {
it('should display empty content when process list is empty', async () => {
const emptyList = { list: { entries: [] } };
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(emptyList));
fixture.detectChanges();
const loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent).toBeFalsy();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent.nativeElement).toBeDefined();
});
it('should load spinner and show the content', () => {
it('should load spinner and show the content', async () => {
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
@@ -140,8 +141,7 @@ describe('ServiceTaskListCloudComponent', () => {
component.ngOnChanges({ appName });
fixture.detectChanges();
const loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent).toBeFalsy();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent).toBeFalsy();
@@ -235,7 +235,6 @@ describe('ServiceTaskListCloudComponent', () => {
});
describe('component changes', () => {
beforeEach(() => {
component.rows = fakeServiceTask.list.entries;
fixture.detectChanges();
@@ -283,13 +282,15 @@ describe('ServiceTaskListCloudComponent', () => {
it('should reload task list when sorting on a column changes', () => {
const getServiceTaskByRequestSpy = spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
component.onSortingChanged(new CustomEvent('sorting-changed', {
detail: {
key: 'fakeName',
direction: 'asc'
},
bubbles: true
}));
component.onSortingChanged(
new CustomEvent('sorting-changed', {
detail: {
key: 'fakeName',
direction: 'asc'
},
bubbles: true
})
);
fixture.detectChanges();
expect(component.sorting).toEqual([
new TaskListCloudSortingModel({
@@ -307,15 +308,14 @@ describe('ServiceTaskListCloudComponent', () => {
const size = component.size;
const skipCount = component.skipCount;
component.pagination.pipe(skip(3))
.subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount);
done();
});
component.pagination.pipe(skip(3)).subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount);
done();
});
const pagination = {
maxItems: 250,
@@ -334,15 +334,14 @@ describe('ServiceTaskListCloudComponent', () => {
maxItems: 250,
skipCount: 200
};
component.pagination.pipe(skip(1))
.subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done();
});
component.pagination.pipe(skip(1)).subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done();
});
component.updatePagination(pagination);
});
@@ -358,14 +357,8 @@ describe('ServiceTaskListCloudComponent: Injecting custom columns for task list
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
],
declarations: [
CustomTaskListComponent,
CustomCopyContentTaskListComponent
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule],
declarations: [CustomTaskListComponent, CustomCopyContentTaskListComponent]
});
serviceTaskListCloudService = TestBed.inject(ServiceTaskListCloudService);
@@ -396,9 +389,7 @@ describe('ServiceTaskListCloudComponent: Injecting custom columns for task list
customCopyComponent.taskList.reload();
copyFixture.detectChanges();
copyFixture.debugElement
.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]'))
.triggerEventHandler('mouseenter');
copyFixture.debugElement.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]')).triggerEventHandler('mouseenter');
copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).not.toBeNull();
@@ -408,9 +399,7 @@ describe('ServiceTaskListCloudComponent: Injecting custom columns for task list
customCopyComponent.taskList.reload();
copyFixture.detectChanges();
copyFixture.debugElement
.query(By.css('span[title="serviceTaskName"]'))
.triggerEventHandler('mouseenter');
copyFixture.debugElement.query(By.css('span[title="serviceTaskName"]')).triggerEventHandler('mouseenter');
copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).toBeNull();
@@ -426,10 +415,7 @@ describe('ServiceTaskListCloudComponent: Copy cell content directive from app.co
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule]
});
appConfig = TestBed.inject(AppConfigService);
serviceTaskListCloudService = TestBed.inject(ServiceTaskListCloudService);
@@ -457,7 +443,6 @@ describe('ServiceTaskListCloudComponent: Copy cell content directive from app.co
fixture = TestBed.createComponent(ServiceTaskListCloudComponent);
component = fixture.componentInstance;
taskSpy = spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
});
afterEach(() => {
fixture.destroy();
@@ -470,8 +455,7 @@ describe('ServiceTaskListCloudComponent: Copy cell content directive from app.co
component.reload();
fixture.detectChanges();
const columnWithCopyContentFlagTrue = fixture.debugElement
.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]'));
const columnWithCopyContentFlagTrue = fixture.debugElement.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]'));
columnWithCopyContentFlagTrue.triggerEventHandler('mouseenter');
@@ -486,8 +470,7 @@ describe('ServiceTaskListCloudComponent: Copy cell content directive from app.co
component.reload();
fixture.detectChanges();
const columnWithCopyContentFlagNotTrue = fixture.debugElement
.query(By.css('span[title="serviceTaskName"]'));
const columnWithCopyContentFlagNotTrue = fixture.debugElement.query(By.css('span[title="serviceTaskName"]'));
columnWithCopyContentFlagNotTrue.triggerEventHandler('mouseenter');

View File

@@ -18,17 +18,19 @@
import { Component, SimpleChange, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppConfigService,
DataRowEvent,
ObjectDataRow,
User,
DataColumn,
ColumnsSelectorComponent,
AlfrescoApiService,
AlfrescoApiServiceMock,
AppConfigServiceMock,
TranslationService,
TranslationMock } from '@alfresco/adf-core';
import {
AppConfigService,
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';
@@ -43,16 +45,29 @@ import { TaskListCloudModule } from '../task-list-cloud.module';
import { HttpClientModule } from '@angular/common/http';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
@Component({
template: `
<adf-cloud-task-list #taskListCloud>
template: ` <adf-cloud-task-list #taskListCloud>
<data-columns>
<data-column id="name" key="name" title="ADF_CLOUD_TASK_LIST.PROPERTIES.NAME" class="adf-full-width adf-name-column" [order]="3"></data-column>
<data-column
id="name"
key="name"
title="ADF_CLOUD_TASK_LIST.PROPERTIES.NAME"
class="adf-full-width adf-name-column"
[order]="3"
></data-column>
<data-column id="created" key="created" title="ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED" class="adf-hidden"></data-column>
<data-column id="startedBy" key="startedBy" title="ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED" class="adf-desktop-only dw-dt-col-3 adf-ellipsis-cell">
<data-column
id="startedBy"
key="startedBy"
title="ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED"
class="adf-desktop-only dw-dt-col-3 adf-ellipsis-cell"
>
<ng-template let-entry="$implicit">
<div>{{getFullName(entry.row?.obj?.startedBy)}}</div>
<div>{{ getFullName(entry.row?.obj?.startedBy) }}</div>
</ng-template>
</data-column>
</data-columns>
@@ -68,18 +83,16 @@ class CustomTaskListComponent {
}
@Component({
template: `
<adf-cloud-task-list>
<adf-custom-empty-content-template>
<p id="custom-id"></p>
</adf-custom-empty-content-template>
</adf-cloud-task-list>
`
<adf-cloud-task-list>
<adf-custom-empty-content-template>
<p id="custom-id"></p>
</adf-custom-empty-content-template>
</adf-cloud-task-list>
`
})
class EmptyTemplateComponent {
}
class EmptyTemplateComponent {}
@Component({
template: `
<adf-cloud-task-list>
template: ` <adf-cloud-task-list>
<data-columns>
<data-column [copyContent]="true" key="id" title="ADF_CLOUD_TASK_LIST.PROPERTIES.ID"></data-column>
<data-column key="name" title="ADF_CLOUD_TASK_LIST.PROPERTIES.NAME"></data-column>
@@ -92,6 +105,7 @@ class CustomCopyContentTaskListComponent {
}
describe('TaskListCloudComponent', () => {
let loader: HarnessLoader;
let component: TaskListCloudComponent;
let fixture: ComponentFixture<TaskListCloudComponent>;
let appConfig: AppConfigService;
@@ -103,10 +117,7 @@ describe('TaskListCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
],
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule],
providers: [
{
provide: TASK_LIST_CLOUD_TOKEN,
@@ -144,6 +155,7 @@ describe('TaskListCloudComponent', () => {
});
component.isColumnSchemaCreated$ = of(true).pipe(shareReplay(1));
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -162,7 +174,7 @@ describe('TaskListCloudComponent', () => {
expect(component.columns.length).toEqual(3);
});
it('should display empty content when process list is empty', () => {
it('should display empty content when process list is empty', async () => {
const emptyList = { list: { entries: [] } };
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
fixture.detectChanges();
@@ -171,14 +183,13 @@ describe('TaskListCloudComponent', () => {
component.ngOnChanges({ appName });
fixture.detectChanges();
const loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent).toBeFalsy();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent.nativeElement).toBeDefined();
});
it('should load spinner and show the content', () => {
it('should load spinner and show the content', async () => {
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
@@ -186,8 +197,7 @@ describe('TaskListCloudComponent', () => {
component.ngOnChanges({ appName });
fixture.detectChanges();
const loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
expect(loadingContent).toBeFalsy();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent).toBeFalsy();
@@ -374,7 +384,6 @@ describe('TaskListCloudComponent', () => {
});
describe('component changes', () => {
beforeEach(() => {
component.rows = fakeGlobalTasks.list.entries;
fixture.detectChanges();
@@ -431,13 +440,15 @@ describe('TaskListCloudComponent', () => {
it('should reload task list when sorting on a column changes', () => {
const getTaskByRequestSpy = spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
component.onSortingChanged(new CustomEvent('sorting-changed', {
detail: {
key: 'fakeName',
direction: 'asc'
},
bubbles: true
}));
component.onSortingChanged(
new CustomEvent('sorting-changed', {
detail: {
key: 'fakeName',
direction: 'asc'
},
bubbles: true
})
);
fixture.detectChanges();
expect(component.sorting).toEqual([
new TaskListCloudSortingModel({
@@ -455,15 +466,14 @@ describe('TaskListCloudComponent', () => {
const size = component.size;
const skipCount = component.skipCount;
component.pagination.pipe(skip(3))
.subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount);
done();
});
component.pagination.pipe(skip(3)).subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount);
done();
});
const pagination = {
maxItems: 250,
@@ -482,15 +492,14 @@ describe('TaskListCloudComponent', () => {
maxItems: 250,
skipCount: 200
};
component.pagination.pipe(skip(1))
.subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done();
});
component.pagination.pipe(skip(1)).subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done();
});
component.updatePagination(pagination);
});
@@ -506,14 +515,8 @@ describe('TaskListCloudComponent: Injecting custom colums for tasklist - CustomT
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
],
declarations: [
CustomTaskListComponent,
CustomCopyContentTaskListComponent
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule],
declarations: [CustomTaskListComponent, CustomCopyContentTaskListComponent]
});
taskListCloudService = TestBed.inject(TASK_LIST_CLOUD_TOKEN);
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
@@ -543,9 +546,7 @@ describe('TaskListCloudComponent: Injecting custom colums for tasklist - CustomT
customCopyComponent.taskList.reload();
copyFixture.detectChanges();
copyFixture.debugElement
.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'))
.triggerEventHandler('mouseenter');
copyFixture.debugElement.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]')).triggerEventHandler('mouseenter');
copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).not.toBeNull();
@@ -555,9 +556,7 @@ describe('TaskListCloudComponent: Injecting custom colums for tasklist - CustomT
customCopyComponent.taskList.reload();
copyFixture.detectChanges();
copyFixture.debugElement
.query(By.css('span[title="standalone-subtask"]'))
.triggerEventHandler('mouseenter');
copyFixture.debugElement.query(By.css('span[title="standalone-subtask"]')).triggerEventHandler('mouseenter');
copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).toBeNull();
@@ -570,12 +569,7 @@ describe('TaskListCloudComponent: Creating an empty custom template - EmptyTempl
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule,
NoopAnimationsModule,
TranslateModule.forRoot(),
TaskListCloudModule
],
imports: [HttpClientModule, NoopAnimationsModule, TranslateModule.forRoot(), TaskListCloudModule],
providers: [
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
{ provide: AppConfigService, useClass: AppConfigServiceMock },
@@ -612,10 +606,7 @@ describe('TaskListCloudComponent: Copy cell content directive from app.config sp
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessServiceCloudTestingModule
]
imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule]
});
appConfig = TestBed.inject(AppConfigService);
taskListCloudService = TestBed.inject(TASK_LIST_CLOUD_TOKEN);
@@ -663,8 +654,7 @@ describe('TaskListCloudComponent: Copy cell content directive from app.config sp
component.reload();
fixture.detectChanges();
const columnWithCopyContentFlagTrue = fixture.debugElement
.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'));
const columnWithCopyContentFlagTrue = fixture.debugElement.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'));
columnWithCopyContentFlagTrue.triggerEventHandler('mouseenter');
@@ -679,8 +669,7 @@ describe('TaskListCloudComponent: Copy cell content directive from app.config sp
component.reload();
fixture.detectChanges();
const cell = fixture.debugElement
.query(By.css('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]'));
const cell = fixture.debugElement.query(By.css('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]'));
expect(cell.nativeElement.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE');
});
@@ -695,15 +684,20 @@ describe('TaskListCloudComponent: Copy cell content directive from app.config sp
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
expect(emptyContent.nativeElement).toBeDefined();
expect(component.replacePriorityValues({
obj: {},
isSelected: false,
hasValue: () => false,
getValue: () => undefined
}, {
type: 'text',
key: 'priority'
})).toEqual(undefined);
expect(
component.replacePriorityValues(
{
obj: {},
isSelected: false,
hasValue: () => false,
getValue: () => undefined
},
{
type: 'text',
key: 'priority'
}
)
).toEqual(undefined);
});
it('replacePriorityValues should return replaced value when rows are defined', () => {
@@ -714,17 +708,21 @@ describe('TaskListCloudComponent: Copy cell content directive from app.config sp
component.ngOnChanges({ appName });
fixture.detectChanges();
expect(component.replacePriorityValues({
obj: {
priority: 1
},
isSelected: false,
hasValue: () => false,
getValue: () => undefined
}, {
type: 'text',
key: 'priority'
})).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.LOW');
expect(
component.replacePriorityValues(
{
obj: {
priority: 1
},
isSelected: false,
hasValue: () => false,
getValue: () => undefined
},
{
type: 'text',
key: 'priority'
}
)
).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.LOW');
});
});

View File

@@ -35,7 +35,7 @@
<ng-container *ngIf="isLoading(); else empty">
<div class="adf-app-list-spinner">
<mat-spinner></mat-spinner>
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
</div>
</ng-container>

View File

@@ -20,15 +20,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppsProcessService } from './services/apps-process.service';
import { of, throwError } from 'rxjs';
import { defaultApp, deployedApps, nonDeployedApps } from '../mock/apps-list.mock';
import { AppsListComponent, APP_LIST_LAYOUT_GRID, APP_LIST_LAYOUT_LIST } from './apps-list.component';
import { ProcessTestingModule } from '../testing/process.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { AppDefinitionRepresentationModel } from '../task-list';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
describe('AppsListComponent', () => {
let loader: HarnessLoader;
let component: AppsListComponent;
let fixture: ComponentFixture<AppsListComponent>;
let debugElement: DebugElement;
@@ -37,22 +39,18 @@ describe('AppsListComponent', () => {
@Component({
template: `
<adf-apps>
<adf-custom-empty-content-template>
<p id="custom-id">No Apps</p>
</adf-custom-empty-content-template>
</adf-apps>
<adf-apps>
<adf-custom-empty-content-template>
<p id="custom-id">No Apps</p>
</adf-custom-empty-content-template>
</adf-apps>
`
})
class CustomEmptyAppListTemplateComponent {
}
class CustomEmptyAppListTemplateComponent {}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
],
imports: [TranslateModule.forRoot(), ProcessTestingModule],
declarations: [CustomEmptyAppListTemplateComponent]
});
fixture = TestBed.createComponent(AppsListComponent);
@@ -61,6 +59,7 @@ describe('AppsListComponent', () => {
service = TestBed.inject(AppsProcessService);
getAppsSpy = spyOn(service, 'getDeployedApplications').and.returnValue(of(deployedApps));
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
});
it('should define layoutType with the default value', () => {
@@ -79,17 +78,16 @@ describe('AppsListComponent', () => {
});
it('should show the loading spinner when the apps are loading', async () => {
component.loading = true;
spyOn(component, 'isLoading').and.returnValue(true);
fixture.detectChanges();
await fixture.whenStable();
const loadingSpinner = fixture.nativeElement.querySelector('mat-progress-spinner');
expect(loadingSpinner).toBeDefined();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
});
it('should show the apps filtered by defaultAppId', () => {
component.filtersAppId = [{defaultAppId: 'fake-app-1'}];
component.filtersAppId = [{ defaultAppId: 'fake-app-1' }];
fixture.detectChanges();
expect(component.isEmpty()).toBe(false);
expect(component.appList).toBeDefined();
@@ -97,7 +95,7 @@ describe('AppsListComponent', () => {
});
it('should show the apps filtered by deploymentId', () => {
component.filtersAppId = [{deploymentId: '4'}];
component.filtersAppId = [{ deploymentId: '4' }];
fixture.detectChanges();
expect(component.isEmpty()).toBe(false);
expect(component.appList).toBeDefined();
@@ -106,7 +104,7 @@ describe('AppsListComponent', () => {
});
it('should show the apps filtered by name', () => {
component.filtersAppId = [{name: 'App5'}];
component.filtersAppId = [{ name: 'App5' }];
fixture.detectChanges();
expect(component.isEmpty()).toBe(false);
expect(component.appList).toBeDefined();
@@ -115,7 +113,7 @@ describe('AppsListComponent', () => {
});
it('should show the apps filtered by id', () => {
component.filtersAppId = [{id: 6}];
component.filtersAppId = [{ id: 6 }];
fixture.detectChanges();
expect(component.isEmpty()).toBe(false);
expect(component.appList).toBeDefined();
@@ -124,7 +122,7 @@ describe('AppsListComponent', () => {
});
it('should show the apps filtered by modelId', () => {
component.filtersAppId = [{modelId: 66}];
component.filtersAppId = [{ modelId: 66 }];
fixture.detectChanges();
expect(component.isEmpty()).toBe(false);
expect(component.appList).toBeDefined();
@@ -133,7 +131,7 @@ describe('AppsListComponent', () => {
});
it('should show the apps filtered by tenantId', () => {
component.filtersAppId = [{tenantId: 9}];
component.filtersAppId = [{ tenantId: 9 }];
fixture.detectChanges();
expect(component.isEmpty()).toBe(false);
expect(component.appList).toBeDefined();
@@ -149,7 +147,6 @@ describe('AppsListComponent', () => {
});
describe('internationalization', () => {
it('should provide a translation for the default application name, when app name is not provided', () => {
const appDataMock = {
defaultAppId: 'tasks',
@@ -173,7 +170,6 @@ describe('AppsListComponent', () => {
});
describe('layout', () => {
it('should display a grid by default', () => {
fixture.detectChanges();
expect(component.isGrid()).toBe(true);
@@ -201,7 +197,6 @@ describe('AppsListComponent', () => {
});
describe('display apps', () => {
it('should display all deployed apps', () => {
getAppsSpy.and.returnValue(of(deployedApps));
fixture.detectChanges();
@@ -219,10 +214,9 @@ describe('AppsListComponent', () => {
fixture.detectChanges();
expect(debugElement.queryAll(By.css('h1')).length).toBe(1);
});
});
});
describe('select apps', () => {
beforeEach(() => {
getAppsSpy.and.returnValue(of(deployedApps));
fixture.detectChanges();
@@ -252,7 +246,7 @@ describe('AppsListComponent', () => {
const appEls = debugElement.queryAll(By.css('.adf-app-listgrid > div'));
expect(appEls[1].query(By.css('.adf-app-listgrid-item-card-actions-icon'))).not.toBeNull();
});
});
});
describe('Custom CustomEmptyAppListTemplateComponent', () => {
let customFixture: ComponentFixture<CustomEmptyAppListTemplateComponent>;

View File

@@ -22,6 +22,7 @@ import { Observable, Observer, of, Subject } from 'rxjs';
import { AppDefinitionRepresentationModel } from '../task-list';
import { IconModel } from './icon.model';
import { share, takeUntil, finalize } from 'rxjs/operators';
import { AppDefinitionRepresentation } from '@alfresco/js-api';
const DEFAULT_TASKS_APP: string = 'tasks';
const DEFAULT_TASKS_APP_NAME: string = 'ADF_TASK_LIST.APPS.TASK_APP_NAME';
@@ -39,7 +40,6 @@ export const APP_LIST_LAYOUT_GRID: string = 'GRID';
host: { class: 'adf-apps' }
})
export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
@ContentChild(CustomEmptyContentTemplateDirective)
emptyCustomContent: CustomEmptyContentTemplateDirective;
@@ -64,19 +64,16 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
apps$: Observable<AppDefinitionRepresentationModel>;
currentApp: AppDefinitionRepresentationModel;
appList: AppDefinitionRepresentationModel [] = [];
appList: AppDefinitionRepresentationModel[] = [];
loading: boolean = false;
hasEmptyCustomContentTemplate: boolean = false;
private appsObserver: Observer<AppDefinitionRepresentationModel>;
private appsObserver: Observer<AppDefinitionRepresentation>;
private iconsMDL: IconModel;
private onDestroy$ = new Subject<boolean>();
constructor(
private appsProcessService: AppsProcessService,
private translationService: TranslationService) {
this.apps$ = new Observable<AppDefinitionRepresentationModel>((observer) => this.appsObserver = observer)
.pipe(share());
constructor(private appsProcessService: AppsProcessService, private translationService: TranslationService) {
this.apps$ = new Observable<AppDefinitionRepresentationModel>((observer) => (this.appsObserver = observer)).pipe(share());
}
ngOnInit() {
@@ -84,9 +81,7 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
this.setDefaultLayoutType();
}
this.apps$
.pipe(takeUntil(this.onDestroy$))
.subscribe((app: any) => this.appList.push(app));
this.apps$.pipe(takeUntil(this.onDestroy$)).subscribe((app) => this.appList.push(app));
this.iconsMDL = new IconModel();
this.load();
@@ -103,14 +98,12 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
}
}
isDefaultApp(app: AppDefinitionRepresentationModel) {
isDefaultApp(app: AppDefinitionRepresentation): boolean {
return app.defaultAppId === DEFAULT_TASKS_APP;
}
getAppName(app: AppDefinitionRepresentationModel) {
return this.isDefaultApp(app)
? this.translationService.get(DEFAULT_TASKS_APP_NAME)
: of(app.name);
getAppName(app: AppDefinitionRepresentationModel): Observable<string> {
return this.isDefaultApp(app) ? this.translationService.get(DEFAULT_TASKS_APP_NAME) : of(app.name);
}
/**
@@ -130,7 +123,7 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
* @returns `true` if application is selected, otherwise `false`
*/
isSelected(appId: number): boolean {
return (this.currentApp !== undefined && appId === this.currentApp.id);
return this.currentApp !== undefined && appId === this.currentApp.id;
}
/**
@@ -187,9 +180,9 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
this.loading = true;
this.appsProcessService
.getDeployedApplications()
.pipe(finalize(() => this.loading = false))
.pipe(finalize(() => (this.loading = false)))
.subscribe(
(res: AppDefinitionRepresentationModel[]) => {
(res) => {
this.filterApps(res).forEach((app) => {
if (this.isDefaultApp(app)) {
app.theme = DEFAULT_TASKS_APP_THEME;
@@ -206,24 +199,28 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy {
);
}
private filterApps(apps: AppDefinitionRepresentationModel []): AppDefinitionRepresentationModel[] {
const filteredApps: AppDefinitionRepresentationModel[] = [];
private filterApps(apps: AppDefinitionRepresentation[]): AppDefinitionRepresentation[] {
if (this.filtersAppId) {
apps.filter((app: AppDefinitionRepresentationModel) => {
const filteredApps: AppDefinitionRepresentation[] = [];
apps.forEach((app) => {
this.filtersAppId.forEach((filter) => {
if (app.defaultAppId === filter.defaultAppId ||
if (
app.defaultAppId === filter.defaultAppId ||
app.deploymentId === filter.deploymentId ||
app.name === filter.name ||
app.id === filter.id ||
app.modelId === filter.modelId ||
app.tenantId === filter.tenantId) {
app.tenantId === filter.tenantId
) {
filteredApps.push(app);
}
});
});
} else {
return apps;
return filteredApps;
}
return filteredApps;
return apps;
}
}

View File

@@ -22,21 +22,11 @@ import { CoreModule } from '@alfresco/adf-core';
import { AppsListComponent } from './apps-list.component';
import { SelectAppsDialogComponent } from './select-apps-dialog.component';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({
imports: [
CommonModule,
MaterialModule,
CoreModule
],
declarations: [
AppsListComponent,
SelectAppsDialogComponent
],
exports: [
AppsListComponent,
SelectAppsDialogComponent
]
imports: [CommonModule, MaterialModule, CoreModule, MatProgressSpinnerModule],
declarations: [AppsListComponent, SelectAppsDialogComponent],
exports: [AppsListComponent, SelectAppsDialogComponent]
})
export class AppsListModule {
}
export class AppsListModule {}

View File

@@ -17,6 +17,7 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { ProcessContentService } from '../form/services/process-content.service';
import { RelatedContentRepresentation } from '@alfresco/js-api';
@Component({
selector: 'adf-create-process-attachment',
@@ -40,7 +41,7 @@ export class CreateProcessAttachmentComponent implements OnChanges {
* from within the component.
*/
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
success = new EventEmitter<RelatedContentRepresentation>();
constructor(private activitiContentService: ProcessContentService) {}

View File

@@ -24,7 +24,6 @@ import { TranslateModule } from '@ngx-translate/core';
import { ProcessContentService } from '../form/services/process-content.service';
describe('AttachmentComponent', () => {
let service: ProcessContentService;
let component: AttachmentComponent;
let fixture: ComponentFixture<AttachmentComponent>;
@@ -32,30 +31,28 @@ describe('AttachmentComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
]
imports: [TranslateModule.forRoot(), ProcessTestingModule]
});
fixture = TestBed.createComponent(AttachmentComponent);
component = fixture.componentInstance;
service = fixture.debugElement.injector.get(ProcessContentService);
createTaskRelatedContentSpy = spyOn(service, 'createTaskRelatedContent').and.returnValue(of(
{
createTaskRelatedContentSpy = spyOn(service, 'createTaskRelatedContent').and.returnValue(
of({
status: true
}));
} as any)
);
});
it('should not call createTaskRelatedContent service when taskId changed', () => {
const change = new SimpleChange(null, '123', true);
component.ngOnChanges({taskId: change});
component.ngOnChanges({ taskId: change });
expect(createTaskRelatedContentSpy).not.toHaveBeenCalled();
});
it('should not call createTaskRelatedContent service when there is no file uploaded', () => {
const change = new SimpleChange(null, '123', true);
component.ngOnChanges({taskId: change});
component.ngOnChanges({ taskId: change });
const customEvent: any = {
detail: {
files: []
@@ -67,13 +64,11 @@ describe('AttachmentComponent', () => {
it('should call createTaskRelatedContent service when there is a file uploaded', () => {
const change = new SimpleChange(null, '123', true);
component.ngOnChanges({taskId: change});
component.ngOnChanges({ taskId: change });
const file = new File([new Blob()], 'Test');
const customEvent = {
detail: {
files: [
file
]
files: [file]
}
};
component.onFileUpload(customEvent);

View File

@@ -24,9 +24,12 @@ import { ProcessTestingModule } from '../testing/process.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { mockEmittedProcessAttachments, mockProcessAttachments } from '../mock/process/process-attachments.mock';
import { ProcessContentService } from '../form/services/process-content.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatMenuHarness } from '@angular/material/menu/testing';
describe('ProcessAttachmentListComponent', () => {
let loader: HarnessLoader;
let service: ProcessContentService;
let component: ProcessAttachmentListComponent;
let fixture: ComponentFixture<ProcessAttachmentListComponent>;
@@ -34,10 +37,7 @@ describe('ProcessAttachmentListComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
]
imports: [TranslateModule.forRoot(), ProcessTestingModule]
});
fixture = TestBed.createComponent(ProcessAttachmentListComponent);
component = fixture.componentInstance;
@@ -48,6 +48,7 @@ describe('ProcessAttachmentListComponent', () => {
const blobObj = new Blob();
spyOn(service, 'getFileRawContent').and.returnValue(of(blobObj));
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
@@ -101,17 +102,15 @@ describe('ProcessAttachmentListComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const actionButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="action_menu_0"]');
actionButton.click();
const menu = await loader.getHarness(MatMenuHarness);
await menu.open();
fixture.detectChanges();
await fixture.whenStable();
const menuItems = await menu.getItems();
expect(menuItems.length).toBe(3);
const actionMenu = window.document.querySelectorAll('button.mat-menu-item').length;
expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull();
expect(actionMenu).toBe(3);
expect(await menuItems[0].getText()).toBe('ADF_PROCESS_LIST.MENU_ACTIONS.VIEW_CONTENT');
expect(await menuItems[1].getText()).toBe('ADF_PROCESS_LIST.MENU_ACTIONS.REMOVE_CONTENT');
expect(await menuItems[2].getText()).toBe('ADF_PROCESS_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT');
});
it('should not display remove action if attachments are read only', async () => {
@@ -122,39 +121,43 @@ describe('ProcessAttachmentListComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const actionButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="action_menu_0"]');
actionButton.click();
fixture.detectChanges();
await fixture.whenStable();
const menu = await loader.getHarness(MatMenuHarness);
await menu.open();
const actionMenu = window.document.querySelectorAll('button.mat-menu-item').length;
expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull();
expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).toBeNull();
expect(actionMenu).toBe(2);
const menuItems = await menu.getItems();
expect(menuItems.length).toBe(2);
expect(await menuItems[0].getText()).toBe('ADF_PROCESS_LIST.MENU_ACTIONS.VIEW_CONTENT');
expect(await menuItems[1].getText()).toBe('ADF_PROCESS_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT');
});
it('should show the empty list component when the attachments list is empty', async () => {
getProcessRelatedContentSpy.and.returnValue(of({
size: 0,
total: 0,
start: 0,
data: []
}));
getProcessRelatedContentSpy.and.returnValue(
of({
size: 0,
total: 0,
start: 0,
data: []
})
);
const change = new SimpleChange(null, '123', true);
component.ngOnChanges({ processInstanceId: change });
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual('ADF_PROCESS_LIST.PROCESS-ATTACHMENT.EMPTY.HEADER');
expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual(
'ADF_PROCESS_LIST.PROCESS-ATTACHMENT.EMPTY.HEADER'
);
});
it('should not show the empty list drag and drop component when is disabled', async () => {
getProcessRelatedContentSpy.and.returnValue(of({
size: 0,
total: 0,
start: 0,
data: []
}));
getProcessRelatedContentSpy.and.returnValue(
of({
size: 0,
total: 0,
start: 0,
data: []
})
);
const change = new SimpleChange(null, '123', true);
component.ngOnChanges({ processInstanceId: change });
component.disabled = true;
@@ -162,16 +165,20 @@ describe('ProcessAttachmentListComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement.querySelector('adf-empty-list .adf-empty-list-drag_drop')).toBeNull();
expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual('ADF_PROCESS_LIST.PROCESS-ATTACHMENT.EMPTY.HEADER');
expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual(
'ADF_PROCESS_LIST.PROCESS-ATTACHMENT.EMPTY.HEADER'
);
});
it('should show the empty list component when the attachments list is empty for completed process', async () => {
getProcessRelatedContentSpy.and.returnValue(of({
size: 0,
total: 0,
start: 0,
data: []
}));
getProcessRelatedContentSpy.and.returnValue(
of({
size: 0,
total: 0,
start: 0,
data: []
})
);
const change = new SimpleChange(null, '123', true);
component.ngOnChanges({ processInstanceId: change });
component.disabled = true;
@@ -179,8 +186,9 @@ describe('ProcessAttachmentListComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim())
.toEqual('ADF_PROCESS_LIST.PROCESS-ATTACHMENT.EMPTY.HEADER');
expect(fixture.nativeElement.querySelector('div[adf-empty-list-header]').innerText.trim()).toEqual(
'ADF_PROCESS_LIST.PROCESS-ATTACHMENT.EMPTY.HEADER'
);
});
it('should not show the empty list component when the attachments list is not empty for completed process', async () => {
@@ -204,7 +212,6 @@ describe('ProcessAttachmentListComponent', () => {
});
describe('change detection', () => {
const change = new SimpleChange('123', '456', true);
const nullChange = new SimpleChange('123', null, true);
@@ -241,18 +248,14 @@ describe('ProcessAttachmentListComponent', () => {
</adf-process-attachment-list>
`
})
class CustomEmptyTemplateComponent {
}
class CustomEmptyTemplateComponent {}
describe('Custom CustomEmptyTemplateComponent', () => {
let fixture: ComponentFixture<CustomEmptyTemplateComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ProcessTestingModule
],
imports: [TranslateModule.forRoot(), ProcessTestingModule],
declarations: [CustomEmptyTemplateComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});

View File

@@ -205,7 +205,7 @@ export class TaskAttachmentListComponent implements OnChanges, AfterContentInit
this.reset();
const isRelatedContent = 'true';
this.activitiContentService.getTaskRelatedContent(taskId, { isRelatedContent }).subscribe(
(res: any) => {
(res) => {
const attachList = [];
res.data.forEach((content) => {
attachList.push({

View File

@@ -15,9 +15,14 @@
* limitations under the License.
*/
import { AlfrescoApiService, LogService } from '@alfresco/adf-core';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
import { ActivitiContentApi, RelatedContentRepresentation, ResultListDataRepresentationRelatedProcessTask } from '@alfresco/js-api';
import {
ActivitiContentApi,
RelatedContentRepresentation,
ResultListDataRepresentationRelatedProcessTask,
ResultListDataRepresentationRelatedContentRepresentation
} from '@alfresco/js-api';
import { Observable, from, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@@ -25,7 +30,6 @@ import { catchError } from 'rxjs/operators';
providedIn: 'root'
})
export class ProcessContentService {
static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error';
static GENERIC_ERROR_MESSAGE: string = 'Server error';
@@ -35,9 +39,7 @@ export class ProcessContentService {
return this._contentApi;
}
constructor(private apiService: AlfrescoApiService,
private logService: LogService) {
}
constructor(private apiService: AlfrescoApiService) {}
/**
* Create temporary related content from an uploaded file.
@@ -46,8 +48,7 @@ export class ProcessContentService {
* @returns The created content data
*/
createTemporaryRawRelatedContent(file: any): Observable<RelatedContentRepresentation> {
return from(this.contentApi.createTemporaryRawRelatedContent(file))
.pipe(catchError((err) => this.handleError(err)));
return from(this.contentApi.createTemporaryRawRelatedContent(file)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -57,8 +58,7 @@ export class ProcessContentService {
* @returns Metadata for the content
*/
getFileContent(contentId: number): Observable<RelatedContentRepresentation> {
return from(this.contentApi.getContent(contentId))
.pipe(catchError((err) => this.handleError(err)));
return from(this.contentApi.getContent(contentId)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -68,8 +68,7 @@ export class ProcessContentService {
* @returns Binary data of the related content
*/
getFileRawContent(contentId: number): Observable<Blob> {
return from(this.contentApi.getRawContent(contentId))
.pipe(catchError((err) => this.handleError(err)));
return from(this.contentApi.getRawContent(contentId)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -118,8 +117,7 @@ export class ProcessContentService {
* @returns Binary data of the thumbnail image
*/
getContentThumbnail(contentId: number): Observable<Blob> {
return from(this.contentApi.getRawContent(contentId, 'thumbnail'))
.pipe(catchError((err) => this.handleError(err)));
return from(this.contentApi.getRawContent(contentId, 'thumbnail')).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -129,9 +127,8 @@ export class ProcessContentService {
* @param opts Options supported by JS-API
* @returns Metadata for the content
*/
getTaskRelatedContent(taskId: string, opts?: any): Observable<any> {
return from(this.contentApi.getRelatedContentForTask(taskId, opts))
.pipe(catchError((err) => this.handleError(err)));
getTaskRelatedContent(taskId: string, opts?: any): Observable<ResultListDataRepresentationRelatedContentRepresentation> {
return from(this.contentApi.getRelatedContentForTask(taskId, opts)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -141,9 +138,8 @@ export class ProcessContentService {
* @param opts Options supported by JS-API
* @returns Metadata for the content
*/
getProcessRelatedContent(processId: string, opts?: any): Observable<any> {
return from(this.contentApi.getRelatedContentForProcessInstance(processId, opts))
.pipe(catchError((err) => this.handleError(err)));
getProcessRelatedContent(processId: string, opts?: any): Observable<ResultListDataRepresentationRelatedContentRepresentation> {
return from(this.contentApi.getRelatedContentForProcessInstance(processId, opts)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -153,8 +149,7 @@ export class ProcessContentService {
* @returns Null response that notifies when the deletion is complete
*/
deleteRelatedContent(contentId: number): Observable<any> {
return from(this.contentApi.deleteContent(contentId))
.pipe(catchError((err) => this.handleError(err)));
return from(this.contentApi.deleteContent(contentId)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -165,9 +160,10 @@ export class ProcessContentService {
* @param opts Options supported by JS-API
* @returns Details of created content
*/
createProcessRelatedContent(processInstanceId: string, content: any, opts?: any): Observable<any> {
return from(this.contentApi.createRelatedContentOnProcessInstance(processInstanceId, content, opts))
.pipe(catchError((err) => this.handleError(err)));
createProcessRelatedContent(processInstanceId: string, content: any, opts?: any): Observable<RelatedContentRepresentation> {
return from(this.contentApi.createRelatedContentOnProcessInstance(processInstanceId, content, opts)).pipe(
catchError((err) => this.handleError(err))
);
}
/**
@@ -178,9 +174,8 @@ export class ProcessContentService {
* @param opts Options supported by JS-API
* @returns Details of created content
*/
createTaskRelatedContent(taskId: string, file: any, opts?: any) {
return from(this.contentApi.createRelatedContentOnTask(taskId, file, opts))
.pipe(catchError((err) => this.handleError(err)));
createTaskRelatedContent(taskId: string, file: any, opts?: any): Observable<RelatedContentRepresentation> {
return from(this.contentApi.createRelatedContentOnTask(taskId, file, opts)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -192,7 +187,12 @@ export class ProcessContentService {
* @param page - page number
* @returns Promise<ResultListDataRepresentationRelatedProcessTask>
*/
getProcessesAndTasksOnContent(sourceId: string, source: string, size?: number, page?: number): Observable<ResultListDataRepresentationRelatedProcessTask> {
getProcessesAndTasksOnContent(
sourceId: string,
source: string,
size?: number,
page?: number
): Observable<ResultListDataRepresentationRelatedProcessTask> {
return from(this.contentApi.getProcessesAndTasksOnContent(sourceId, source, size, page)).pipe(catchError((err) => this.handleError(err)));
}
@@ -231,11 +231,12 @@ export class ProcessContentService {
handleError(error: any): Observable<any> {
let errMsg = ProcessContentService.UNKNOWN_ERROR_MESSAGE;
if (error) {
errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : ProcessContentService.GENERIC_ERROR_MESSAGE;
errMsg = error.message
? error.message
: error.status
? `${error.status} - ${error.statusText}`
: ProcessContentService.GENERIC_ERROR_MESSAGE;
}
this.logService.error(errMsg);
return throwError(errMsg);
}
}

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
export const mockProcessAttachments = {
export const mockProcessAttachments: any = {
size: 2,
total: 2,
start: 0,

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
export const mockTaskAttachments = {
export const mockTaskAttachments: any = {
size: 2,
total: 2,
start: 0,