mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-16 18:13:06 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
124ed9ce69 | ||
|
|
d2cf66cb5d | ||
|
|
0a4a0679c9 | ||
|
|
c26ee4f1aa | ||
|
|
6c55cc50bd | ||
|
|
37e067cc56 | ||
|
|
9755933b46 | ||
|
|
e701daad77 | ||
|
|
720280d612 | ||
|
|
1d555dfcd1 | ||
|
|
1440455eb9 | ||
|
|
a3019b1602 |
@@ -38,10 +38,6 @@ inputs:
|
||||
apa-proxy: #
|
||||
description: "proxy host"
|
||||
required: true
|
||||
deps:
|
||||
description: "Library dependencies"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -125,7 +121,6 @@ runs:
|
||||
PROVIDER: "${{ inputs.e2e-test-provider }}"
|
||||
AUTH_TYPE: "${{ inputs.e2e-test-auth }}"
|
||||
E2E_TEST_ID: "${{ inputs.e2e-test-id }}"
|
||||
DEPS: "${{ inputs.deps }}"
|
||||
shell: bash
|
||||
run: |
|
||||
set -u;
|
||||
@@ -139,7 +134,6 @@ runs:
|
||||
PROVIDER: "${{ inputs.e2e-test-provider }}"
|
||||
AUTH_TYPE: "${{ inputs.e2e-test-auth }}"
|
||||
E2E_TEST_ID: "${{ inputs.e2e-test-id }}"
|
||||
DEPS: "${{ inputs.deps }}"
|
||||
|
||||
uses: nick-fields/retry@v2.8.2
|
||||
with:
|
||||
@@ -155,9 +149,9 @@ runs:
|
||||
export DISPLAY=:99
|
||||
chromedriver --url-base=/wd/hub &
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
|
||||
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "$DEPS" "browser" || exit 1
|
||||
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "browser" || exit 1
|
||||
else
|
||||
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "$DEPS" || exit 1
|
||||
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" || exit 1
|
||||
fi
|
||||
|
||||
- name: upload artifacts on gh
|
||||
|
||||
@@ -18,3 +18,10 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
repo_to_update: alfresco-applications
|
||||
|
||||
adf-upstream-alpha-hxp:
|
||||
name: trigger upstream alpha for hxp monorepo
|
||||
uses: ./.github/workflows/adf_alpha.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
repo_to_update: alfresco-apps
|
||||
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
- name: Get branch name
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@v1.36.0
|
||||
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- name: install
|
||||
@@ -258,7 +258,7 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: ./.github/actions/download-node-modules-and-artifacts
|
||||
- run: nx affected:build $NX_CALCULATION_FLAGS --prod
|
||||
@@ -302,49 +302,42 @@ jobs:
|
||||
auth: "OAUTH"
|
||||
check-cs-env: "true"
|
||||
check-ps-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Content: Components"
|
||||
test-id: "content-services"
|
||||
folder: "content-services/components"
|
||||
provider: "ECM"
|
||||
auth: "BASIC"
|
||||
check-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Content: Directives"
|
||||
test-id: "content-services"
|
||||
folder: "content-services/directives"
|
||||
provider: "ECM"
|
||||
auth: "BASIC"
|
||||
check-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Content: Document List"
|
||||
test-id: "content-services"
|
||||
folder: "content-services/document-list"
|
||||
provider: "ECM"
|
||||
auth: "BASIC"
|
||||
check-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Content: Metadata"
|
||||
test-id: "content-services"
|
||||
folder: "content-services/metadata"
|
||||
provider: "ECM"
|
||||
auth: "BASIC"
|
||||
check-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Content: Upload and Versioning"
|
||||
test-id: "content-services"
|
||||
folder: "content-services/upload"
|
||||
provider: "ECM"
|
||||
auth: "BASIC"
|
||||
check-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Search"
|
||||
test-id: "content-services"
|
||||
folder: "search"
|
||||
provider: "ECM"
|
||||
auth: "BASIC"
|
||||
check-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process: Form"
|
||||
test-id: "process-services"
|
||||
folder: "process-services/form"
|
||||
@@ -352,7 +345,6 @@ jobs:
|
||||
auth: "OAUTH"
|
||||
check-ps-env: "true"
|
||||
check-external-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process: Process"
|
||||
test-id: "process-services"
|
||||
folder: "process-services/process"
|
||||
@@ -360,7 +352,6 @@ jobs:
|
||||
auth: "OAUTH"
|
||||
check-ps-env: "true"
|
||||
check-external-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process: Tasks"
|
||||
test-id: "process-services"
|
||||
folder: "process-services/tasks"
|
||||
@@ -368,7 +359,6 @@ jobs:
|
||||
auth: "OAUTH"
|
||||
check-ps-env: "true"
|
||||
check-external-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process: Widget"
|
||||
test-id: "process-services"
|
||||
folder: "process-services/widgets"
|
||||
@@ -376,7 +366,6 @@ jobs:
|
||||
auth: "OAUTH"
|
||||
check-ps-env: "true"
|
||||
check-external-cs-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process Cloud: Form"
|
||||
test-id: "process-services-cloud"
|
||||
folder: "process-services-cloud/form-field"
|
||||
@@ -385,7 +374,6 @@ jobs:
|
||||
apa-proxy: true
|
||||
check-cs-env: "true"
|
||||
check-ps-cloud-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process Cloud: People"
|
||||
test-id: "process-services-cloud"
|
||||
folder: "process-services-cloud/people"
|
||||
@@ -394,7 +382,6 @@ jobs:
|
||||
apa-proxy: true
|
||||
check-cs-env: "true"
|
||||
check-ps-cloud-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process Cloud: Process"
|
||||
test-id: "process-services-cloud"
|
||||
folder: "process-services-cloud/process"
|
||||
@@ -403,7 +390,6 @@ jobs:
|
||||
apa-proxy: true
|
||||
check-cs-env: "true"
|
||||
check-ps-cloud-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process Cloud: Start Task"
|
||||
test-id: "process-services-cloud"
|
||||
folder: "process-services-cloud/start-task"
|
||||
@@ -412,7 +398,6 @@ jobs:
|
||||
apa-proxy: true
|
||||
check-cs-env: "true"
|
||||
check-ps-cloud-env: "true"
|
||||
deps: "testing"
|
||||
- description: "Process Cloud: Tasks List"
|
||||
test-id: "process-services-cloud"
|
||||
folder: "process-services-cloud/task-list"
|
||||
@@ -421,7 +406,6 @@ jobs:
|
||||
apa-proxy: true
|
||||
check-cs-env: "true"
|
||||
check-ps-cloud-env: "true"
|
||||
deps: "testing"
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -447,7 +431,6 @@ jobs:
|
||||
check-ps-cloud-env: ${{ matrix.e2e-test.check-ps-cloud-env }}
|
||||
check-external-cs-env: ${{ matrix.e2e-test.check-external-cs-env }}
|
||||
apa-proxy: ${{ matrix.e2e-test.apa-proxy }}
|
||||
deps: ${{ matrix.e2e-test.deps }}
|
||||
|
||||
finalize:
|
||||
if: ${{ always() }}
|
||||
|
||||
Vendored
@@ -1476,9 +1476,5 @@
|
||||
"ai:labels",
|
||||
"ai:textLines"
|
||||
]
|
||||
},
|
||||
"viewer": {
|
||||
"enableFileAutoDownload": true,
|
||||
"fileAutoDownloadSizeThresholdInMB": 15
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,18 +653,6 @@
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
<section>
|
||||
<mat-slide-toggle
|
||||
color="primary" [(ngModel)]="enableFileAutoDownload" id="enableFileAutoDownload" (change)="onEnableFileAutoDownloadChange()">
|
||||
Enable FileAutoDownload
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
<section *ngIf="enableFileAutoDownload">
|
||||
<mat-form-field>
|
||||
<input matInput type="number" [(ngModel)]="fileAutoDownloadSizeThresholdInMB" (change)="onFileAutoDownloadSizeThresholdChange()">
|
||||
</mat-form-field>
|
||||
</section>
|
||||
|
||||
<h5>Upload</h5>
|
||||
<section *ngIf="acceptedFilesTypeShow">
|
||||
<mat-form-field floatPlaceholder="float">
|
||||
|
||||
@@ -251,9 +251,6 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
selectedNodes = [];
|
||||
|
||||
enableFileAutoDownload: boolean = this.appConfig.get('viewer.enableFileAutoDownload', true);
|
||||
fileAutoDownloadSizeThresholdInMB: number = this.appConfig.get('viewer.fileAutoDownloadSizeThresholdInMB', 15);
|
||||
|
||||
constructor(private notificationService: NotificationService,
|
||||
private uploadService: UploadService,
|
||||
private contentService: ContentService,
|
||||
@@ -780,14 +777,4 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.selectedNodes = [];
|
||||
}
|
||||
|
||||
onEnableFileAutoDownloadChange() {
|
||||
const previewConfig = this.appConfig?.config['viewer'];
|
||||
previewConfig['enableFileAutoDownload'] = this.enableFileAutoDownload;
|
||||
}
|
||||
|
||||
onFileAutoDownloadSizeThresholdChange() {
|
||||
const previewConfig = this.appConfig?.config['viewer'];
|
||||
previewConfig['fileAutoDownloadSizeThresholdInMB'] = this.fileAutoDownloadSizeThresholdInMB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -790,27 +790,6 @@ This will give the following output:
|
||||
|
||||

|
||||
|
||||
### File Auto downloading
|
||||
|
||||
In case of files exceeding a predefined file size, the document list component can be configured to automatically download those file when trying to preview them.
|
||||
This can help in reducing server load, and ensuring quick access to such files. After turning this feature on, whenever the user tries to preview a file with a large
|
||||
file size, the Document List component will first preview a dialog, asking for confirmation from the user on whether they want to download the file, or cancel the preview altogether.
|
||||
|
||||
In order to configure the Document List to automatically download the files, the following environment variables would need to be set up in app.config.json -
|
||||
|
||||
```
|
||||
"viewer": {
|
||||
"enableFileAutoDownload": true,
|
||||
"fileAutoDownloadSizeThresholdInMB": 15
|
||||
}
|
||||
```
|
||||
|
||||
Here, `"enableFileAutoDownload": true,` would enable the file auto download feature on the Document List component. Setting this flag to false disables this feature, and always
|
||||
triggers a file preview when trying to view a file, regardless of its size.
|
||||
|
||||
The second configuration here, `"fileAutoDownloadSizeThresholdInMB": 15` specifies the file size threshold (in MB), after which the Document List component will start downloading the file.
|
||||
In the example provided above, any file greater than 15MB in size would trigger the auto download functionality. Files lower than 15MB in size would continue to preview normally.
|
||||
|
||||
## See also
|
||||
|
||||
- [Datatable component](../../core/components/datatable.component.md)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
Title: Tags Creator component
|
||||
Added: v6.0.0-A.3
|
||||
Status: Active
|
||||
Last reviewed: 2023-03-27
|
||||
---
|
||||
|
||||
# [Tags Creator component](../../../lib/content-services/src/lib/tag/tags-creator/tags-creator.component.ts "Defined in tags-creator.component.ts")
|
||||
|
||||
Allows to create multiple tags. That component contains input and two lists. Top list is all created tags, bottom list is searched tags based on input's value.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<adf-tags-creator
|
||||
[(tagNameControlVisible)]="tagNameControlVisible"
|
||||
(tagsChange)="tags = $event"
|
||||
[mode]="tagsCreatorMode">
|
||||
</adf-tags-creator>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
|-----------------------|-------------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| mode | `TagsCreatorMode` | | Create mode if only new tags can be created or Create And Assign mode if new tags can be created and existing tags can be selected. |
|
||||
| disabledTagsRemoving | `boolean` | false | False if tags can be removed from top list, true otherwise. |
|
||||
| tags | `string[]` | | Default top list. |
|
||||
| tagNameControlVisible | `boolean` | false | True if input should be visible, false otherwise. | |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
|-----------------------------------|------------------------------------------------------------------------|--------------------------------------------------|
|
||||
| existingTagsPanelVisibilityChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when bottom list is showing or hiding. |
|
||||
| tagsChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string[]>` | Emitted when tags in top list are changed. |
|
||||
| tagNameControlVisibleChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when input is showing or hiding. |
|
||||
|
||||
@@ -61,11 +61,6 @@ Manages tags in Content Services.
|
||||
Find tag which name matches exactly to passed name.
|
||||
- _name:_ `string` - Value for name which should be used during finding exact tag.
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<TagEntry>` - Found tag which name matches exactly to passed name.
|
||||
- **assignTagsToNode**(nodeId: `string`, tags: `TagBody[]`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`|`[`TagEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagEntry.md)`>`<br/>
|
||||
Assign tags to node. If tag is new then tag is also created additionally, if tag already exists then it is just assigned.
|
||||
- _nodeId:_ `string` - Id of node to which tags should be assigned.
|
||||
- _tags:_ `TagBody[]` - List of tags to create and assign or just assign if they already exist.
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`|`[`TagEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagEntry.md)`>` - Just linked tags to node or single tag if linked only one tag.
|
||||
|
||||
## Details
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Title: Edit Process Filter Cloud component
|
||||
Added: v3.0.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2023-04-03
|
||||
Last reviewed: 2019-04-10
|
||||
---
|
||||
|
||||
# [Edit Process Filter Cloud component](../../../lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts "Defined in edit-process-filter-cloud.component.ts")
|
||||
@@ -47,8 +47,6 @@ Shows/edits process filter details.
|
||||
| appName | `string` | "" | The name of the application. |
|
||||
| filterProperties | `string[]` | | List of process filter properties to display |
|
||||
| id | `string` | | Id of the process instance filter. |
|
||||
| environmentId | `string` | | Environment Id of the process instance filter. |
|
||||
| environmentList | `string[]` | | List of environments. |
|
||||
| role | `string` | "" | roles to filter the apps |
|
||||
| showFilterActions | `boolean` | true | Toggles editing of process filter actions. |
|
||||
| showProcessFilterName | `boolean` | true | Toggles the appearance of the process filter name . |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Title: Edit Task Filter Cloud component
|
||||
Added: v3.0.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2023-04-03
|
||||
Last reviewed: 2019-03-27
|
||||
---
|
||||
|
||||
# [Edit Task Filter Cloud component](../../../lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts "Defined in edit-task-filter-cloud.component.ts")
|
||||
@@ -47,8 +47,6 @@ Edits task filter details.
|
||||
| appName | `string` | "" | (required) Name of the app. |
|
||||
| filterProperties | `string[]` | \[] | List of task filter properties to display. |
|
||||
| id | `string` | | (required) ID of the task filter. |
|
||||
| environmentId | `string` | | Environment Id of the task filter. |
|
||||
| environmentList | `string[]` | | List of environments. |
|
||||
| processInstanceId | `string` | | processInstanceId of the task filter. |
|
||||
| role | `string` | "" | user role. |
|
||||
| showFilterActions | `boolean` | true | Toggles the filter actions. |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Title: Apps Process Cloud Service
|
||||
Added: v3.0.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2023-04-03
|
||||
Last reviewed: 2019-01-09
|
||||
---
|
||||
|
||||
# [Apps Process Cloud Service](../../../lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.ts "Defined in apps-process-cloud.service.ts")
|
||||
@@ -24,11 +24,6 @@ Gets details of deployed apps for the current user.
|
||||
|
||||
- **loadApps**()<br/>
|
||||
|
||||
- **getApplicationLabel**(application: `ApplicationInstanceModel`, environmentList?: `Environment[]`): `string`<br/>
|
||||
Gets application label.
|
||||
- application:_ `ApplicationInstanceModel` - Required application
|
||||
- environmentList:_ `Environment[]` - (Optional) to add environment name in front of application name
|
||||
- **Returns** `string` - The application label.
|
||||
## Details
|
||||
|
||||
This service implements some features of the [Apps process service](../../core/services/apps-process.service.md)
|
||||
|
||||
@@ -25,7 +25,6 @@ import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
import { NodeAspectService } from './node-aspect.service';
|
||||
import { DialogAspectListService } from './dialog-aspect-list.service';
|
||||
import { CardViewContentUpdateService } from '../../common/services/card-view-content-update.service';
|
||||
import { TagService } from '@alfresco/adf-content-services';
|
||||
|
||||
describe('NodeAspectService', () => {
|
||||
|
||||
@@ -100,13 +99,4 @@ describe('NodeAspectService', () => {
|
||||
nodeAspectService.updateNodeAspects('fake-node-id');
|
||||
});
|
||||
|
||||
it('should call emit on refresh from TagService', () => {
|
||||
const tagService = TestBed.inject(TagService);
|
||||
spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of([]));
|
||||
const node = new MinimalNode({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
|
||||
spyOn(nodeApiService, 'updateNode').and.returnValue(of(node));
|
||||
spyOn(tagService.refresh, 'emit');
|
||||
nodeAspectService.updateNodeAspects('some node id', 'some-selector');
|
||||
expect(tagService.refresh.emit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,6 @@ import { Injectable } from '@angular/core';
|
||||
import { DialogAspectListService } from './dialog-aspect-list.service';
|
||||
import { CardViewContentUpdateService } from '../../common/services/card-view-content-update.service';
|
||||
import { NodesApiService } from '../../common/services/nodes-api.service';
|
||||
import { TagService } from '../../tag/services/tag.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -28,8 +27,7 @@ export class NodeAspectService {
|
||||
|
||||
constructor(private nodesApiService: NodesApiService,
|
||||
private dialogAspectListService: DialogAspectListService,
|
||||
private cardViewContentUpdateService: CardViewContentUpdateService,
|
||||
private tagService: TagService) {
|
||||
private cardViewContentUpdateService: CardViewContentUpdateService) {
|
||||
}
|
||||
|
||||
updateNodeAspects(nodeId: string, selectorAutoFocusedOnClose?: string) {
|
||||
@@ -37,7 +35,6 @@ export class NodeAspectService {
|
||||
this.nodesApiService.updateNode(nodeId, { aspectNames: [...aspectList] }).subscribe((updatedNode) => {
|
||||
this.nodesApiService.nodeUpdated.next(updatedNode);
|
||||
this.cardViewContentUpdateService.updateNodeAspect(updatedNode);
|
||||
this.tagService.refresh.emit();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+1
-2
@@ -8,8 +8,7 @@
|
||||
[editable]="editable"
|
||||
[multi]="multi"
|
||||
[displayAspect]="displayAspect"
|
||||
[preset]="preset"
|
||||
[displayTags]="true">
|
||||
[preset]="preset">
|
||||
</adf-content-metadata>
|
||||
</mat-card-content>
|
||||
<mat-card-footer class="adf-content-metadata-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
|
||||
+1
-6
@@ -55,8 +55,7 @@ describe('ContentMetadataCardComponent', () => {
|
||||
content: {},
|
||||
properties: {},
|
||||
createdByUser: {},
|
||||
modifiedByUser: {},
|
||||
id: 'some-id'
|
||||
modifiedByUser: {}
|
||||
} as Node;
|
||||
|
||||
component.node = node;
|
||||
@@ -92,10 +91,6 @@ describe('ContentMetadataCardComponent', () => {
|
||||
expect(contentMetadataComponent.node).toBe(node);
|
||||
});
|
||||
|
||||
it('should assign true to displayTags for ContentMetadataComponent', () => {
|
||||
expect(fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance.displayTags).toBeTrue();
|
||||
});
|
||||
|
||||
it('should pass through the preset to the underlying component', () => {
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance;
|
||||
|
||||
|
||||
+1
-30
@@ -19,36 +19,7 @@
|
||||
[multiValueSeparator]="multiValueSeparator">
|
||||
</adf-card-view>
|
||||
</mat-expansion-panel>
|
||||
<ng-container *ngIf="displayTags">
|
||||
<mat-expansion-panel *ngIf="!editable">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{ 'METADATA.BASIC.TAGS' | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<p *ngFor="let tag of tags" class="adf-metadata-properties-tag">{{ tag }}</p>
|
||||
</mat-expansion-panel>
|
||||
<div
|
||||
*ngIf="editable"
|
||||
class="adf-metadata-properties-tags">
|
||||
<div class="adf-metadata-properties-tags-title">
|
||||
<p>{{ 'METADATA.BASIC.TAGS' | translate }}</p>
|
||||
<button
|
||||
data-automation-id="showing-tag-input-button"
|
||||
mat-icon-button
|
||||
[attr.title]="'METADATA.BASIC.ADD_TAG_TOOLTIP' | translate"
|
||||
(click)="tagNameControlVisible = true"
|
||||
[hidden]="tagNameControlVisible || saving">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<adf-tags-creator
|
||||
[(tagNameControlVisible)]="tagNameControlVisible"
|
||||
(tagsChange)="storeTagsToAssign($event)"
|
||||
[mode]="tagsCreatorMode"
|
||||
[tags]="assignedTags"
|
||||
[disabledTagsRemoving]="saving">
|
||||
</adf-tags-creator>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="expanded">
|
||||
<ng-container *ngIf="groupedProperties$ | async; else loading; let groupedProperties">
|
||||
<div *ngFor="let group of groupedProperties; let first = first;"
|
||||
|
||||
-39
@@ -17,45 +17,6 @@
|
||||
.mat-expansion-panel:not([class*='mat-elevation-z']) {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.adf-metadata-properties-tag {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: -14px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
&-tags {
|
||||
padding: 0 10px 0 26px;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
adf-tags-creator {
|
||||
.adf-tags-creation {
|
||||
padding-right: 0;
|
||||
padding-left: 12px;
|
||||
|
||||
.adf-tag {
|
||||
margin-top: -14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.adf-creator-with-existing-tags-panel {
|
||||
background: var(--adf-theme-background-dialog-color);
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-metadata-action-buttons {
|
||||
|
||||
+16
-384
@@ -16,24 +16,23 @@
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ClassesApi, MinimalNode, Node, Tag, TagBody, TagEntry, TagPaging, TagPagingList } from '@alfresco/js-api';
|
||||
import { ClassesApi, MinimalNode, Node } from '@alfresco/js-api';
|
||||
import { ContentMetadataComponent } from './content-metadata.component';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import {
|
||||
CardViewBaseItemModel, CardViewComponent,
|
||||
LogService, setupTestBed, AppConfigService, UpdateNotification
|
||||
LogService, setupTestBed, AppConfigService
|
||||
} from '@alfresco/adf-core';
|
||||
import { NodesApiService } from '../../../common/services/nodes-api.service';
|
||||
import { throwError, of, EMPTY } from 'rxjs';
|
||||
import { throwError, of } from 'rxjs';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { mockGroupProperties } from './mock-data';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service';
|
||||
import { PropertyGroup } from '../../interfaces/property-group.interface';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import { TagsCreatorComponent, TagsCreatorMode, TagService } from '@alfresco/adf-content-services';
|
||||
|
||||
describe('ContentMetadataComponent', () => {
|
||||
let component: ContentMetadataComponent;
|
||||
@@ -43,64 +42,14 @@ describe('ContentMetadataComponent', () => {
|
||||
let nodesApiService: NodesApiService;
|
||||
let node: Node;
|
||||
let folderNode: Node;
|
||||
let tagService: TagService;
|
||||
|
||||
const preset = 'custom-preset';
|
||||
|
||||
const mockTagPaging = (): TagPaging => {
|
||||
const tagPaging = new TagPaging();
|
||||
tagPaging.list = new TagPagingList();
|
||||
const tagEntry1 = new TagEntry();
|
||||
tagEntry1.entry = new Tag();
|
||||
tagEntry1.entry.tag = 'Tag 1';
|
||||
tagEntry1.entry.id = 'some id 1';
|
||||
const tagEntry2 = new TagEntry();
|
||||
tagEntry2.entry = new Tag();
|
||||
tagEntry2.entry.tag = 'Tag 2';
|
||||
tagEntry2.entry.id = 'some id 2';
|
||||
tagPaging.list.entries = [tagEntry1, tagEntry2];
|
||||
return tagPaging;
|
||||
};
|
||||
|
||||
const findTagElements = (): DebugElement[] => fixture.debugElement.queryAll(By.css('.adf-metadata-properties-tag'));
|
||||
|
||||
const findCancelButton = (): HTMLButtonElement => fixture.debugElement.query(By.css('[data-automation-id=reset-metadata]')).nativeElement;
|
||||
|
||||
const clickOnCancel = () => {
|
||||
findCancelButton().click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
const findSaveButton = (): HTMLButtonElement => fixture.debugElement.query(By.css('[data-automation-id=save-metadata]')).nativeElement;
|
||||
|
||||
const clickOnSave = () => {
|
||||
findSaveButton().click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
const findTagsCreator = (): TagsCreatorComponent => fixture.debugElement.query(By.directive(TagsCreatorComponent))?.componentInstance;
|
||||
|
||||
const findShowingTagInputButton = (): HTMLButtonElement =>
|
||||
fixture.debugElement.query(By.css('[data-automation-id=showing-tag-input-button]')).nativeElement;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ContentTestingModule
|
||||
],
|
||||
providers: [{
|
||||
provide: LogService,
|
||||
useValue: {
|
||||
error: jasmine.createSpy('error')
|
||||
}
|
||||
}, {
|
||||
provide: TagService,
|
||||
useValue: {
|
||||
getTagsByNodeId: () => EMPTY,
|
||||
removeTag: () => EMPTY,
|
||||
assignTagsToNode: () => EMPTY
|
||||
}
|
||||
}]
|
||||
providers: [{ provide: LogService, useValue: { error: jasmine.createSpy('error') } }]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -109,7 +58,6 @@ describe('ContentMetadataComponent', () => {
|
||||
contentMetadataService = TestBed.inject(ContentMetadataService);
|
||||
updateService = TestBed.inject(CardViewContentUpdateService);
|
||||
nodesApiService = TestBed.inject(NodesApiService);
|
||||
tagService = TestBed.inject(TagService);
|
||||
|
||||
node = {
|
||||
id: 'node-id',
|
||||
@@ -203,65 +151,14 @@ describe('ContentMetadataComponent', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
clickOnSave();
|
||||
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
|
||||
saveButton.nativeElement.click();
|
||||
|
||||
await fixture.whenStable();
|
||||
expect(component.node).toEqual(expectedNode);
|
||||
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should call removeTag and assignTagsToNode on TagService on save click', fakeAsync( () => {
|
||||
component.editable = true;
|
||||
component.displayTags = true;
|
||||
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
|
||||
const expectedNode = { ...node, name: 'some-modified-value' };
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
|
||||
const tagPaging = mockTagPaging();
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
spyOn(tagService, 'removeTag').and.returnValue(EMPTY);
|
||||
spyOn(tagService, 'assignTagsToNode').and.returnValue(EMPTY);
|
||||
const tagName1 = tagPaging.list.entries[0].entry.tag;
|
||||
const tagName2 = 'New tag 3';
|
||||
|
||||
updateService.update(property, 'updated-value');
|
||||
tick(600);
|
||||
|
||||
fixture.detectChanges();
|
||||
findTagsCreator().tagsChange.emit([tagName1, tagName2]);
|
||||
clickOnSave();
|
||||
|
||||
const tag1 = new TagBody();
|
||||
tag1.tag = tagName1;
|
||||
const tag2 = new TagBody();
|
||||
tag2.tag = tagName2;
|
||||
expect(tagService.removeTag).toHaveBeenCalledWith(node.id, tagPaging.list.entries[1].entry.id);
|
||||
expect(tagService.assignTagsToNode).toHaveBeenCalledWith(node.id, [tag1, tag2]);
|
||||
}));
|
||||
|
||||
it('should call getTagsByNodeId on TagService on save click', fakeAsync( () => {
|
||||
component.editable = true;
|
||||
component.displayTags = true;
|
||||
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
|
||||
const expectedNode = { ...node, name: 'some-modified-value' };
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
|
||||
const tagPaging = mockTagPaging();
|
||||
const getTagsByNodeIdSpy = spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
spyOn(tagService, 'removeTag').and.returnValue(of(undefined));
|
||||
spyOn(tagService, 'assignTagsToNode').and.returnValue(of({}));
|
||||
|
||||
updateService.update(property, 'updated-value');
|
||||
tick(600);
|
||||
|
||||
fixture.detectChanges();
|
||||
findTagsCreator().tagsChange.emit([tagPaging.list.entries[0].entry.tag, 'New tag 3']);
|
||||
getTagsByNodeIdSpy.calls.reset();
|
||||
clickOnSave();
|
||||
|
||||
expect(tagService.getTagsByNodeId).toHaveBeenCalledWith(node.id);
|
||||
}));
|
||||
|
||||
it('should throw error on unsuccessful save', fakeAsync((done) => {
|
||||
const logService: LogService = TestBed.inject(LogService);
|
||||
component.editable = true;
|
||||
@@ -280,7 +177,11 @@ describe('ContentMetadataComponent', () => {
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(throwError(new Error('My bad')));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => clickOnSave());
|
||||
fixture.whenStable().then(() => {
|
||||
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
|
||||
saveButton.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should open the confirm dialog when content type is changed', fakeAsync(() => {
|
||||
@@ -295,7 +196,8 @@ describe('ContentMetadataComponent', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
clickOnSave();
|
||||
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
|
||||
saveButton.nativeElement.click();
|
||||
|
||||
tick(100);
|
||||
expect(component.node).toEqual(expectedNode);
|
||||
@@ -303,39 +205,6 @@ describe('ContentMetadataComponent', () => {
|
||||
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should call removeTag and assignTagsToNode on TagService after confirming confirmation dialog when content type is changed', fakeAsync(() => {
|
||||
component.editable = true;
|
||||
component.displayTags = true;
|
||||
const property = { key: 'nodeType', value: 'ft:sbiruli' } as CardViewBaseItemModel;
|
||||
const expectedNode = { ...node, nodeType: 'ft:sbiruli' };
|
||||
spyOn(contentMetadataService, 'openConfirmDialog').and.returnValue(of(true));
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
|
||||
const tagPaging = mockTagPaging();
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
spyOn(tagService, 'removeTag').and.returnValue(EMPTY);
|
||||
spyOn(tagService, 'assignTagsToNode').and.returnValue(EMPTY);
|
||||
const tagName1 = tagPaging.list.entries[0].entry.tag;
|
||||
const tagName2 = 'New tag 3';
|
||||
|
||||
updateService.update(property, 'ft:poppoli');
|
||||
tick(600);
|
||||
|
||||
fixture.detectChanges();
|
||||
findTagsCreator().tagsChange.emit([tagName1, tagName2]);
|
||||
tick(100);
|
||||
fixture.detectChanges();
|
||||
clickOnSave();
|
||||
|
||||
tick(100);
|
||||
const tag1 = new TagBody();
|
||||
tag1.tag = tagName1;
|
||||
const tag2 = new TagBody();
|
||||
tag2.tag = tagName2;
|
||||
expect(tagService.removeTag).toHaveBeenCalledWith(node.id, tagPaging.list.entries[1].entry.id);
|
||||
expect(tagService.assignTagsToNode).toHaveBeenCalledWith(node.id, [tag1, tag2]);
|
||||
}));
|
||||
|
||||
it('should retrigger the load of the properties when the content type has changed', fakeAsync(() => {
|
||||
component.editable = true;
|
||||
const property = { key: 'nodeType', value: 'ft:sbiruli' } as CardViewBaseItemModel;
|
||||
@@ -349,7 +218,8 @@ describe('ContentMetadataComponent', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
clickOnSave();
|
||||
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
|
||||
saveButton.nativeElement.click();
|
||||
|
||||
tick(100);
|
||||
expect(component.node).toEqual(expectedNode);
|
||||
@@ -905,244 +775,6 @@ describe('ContentMetadataComponent', () => {
|
||||
expect(event.stopPropagation).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tags list', () => {
|
||||
let tagPaging: TagPaging;
|
||||
|
||||
beforeEach(() => {
|
||||
tagPaging = mockTagPaging();
|
||||
component.displayTags = true;
|
||||
});
|
||||
|
||||
it('should render tags after loading tags in ngOnInit', () => {
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(2);
|
||||
expect(tagElements[0].nativeElement.textContent).toBe(tagPaging.list.entries[0].entry.tag);
|
||||
expect(tagElements[1].nativeElement.textContent).toBe(tagPaging.list.entries[1].entry.tag);
|
||||
expect(tagService.getTagsByNodeId).toHaveBeenCalledWith(node.id);
|
||||
});
|
||||
|
||||
it('should not render tags after loading tags in ngOnInit if displayTags is false', () => {
|
||||
component.displayTags = false;
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(0);
|
||||
expect(tagService.getTagsByNodeId).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render tags after loading tags in ngOnChanges', () => {
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
|
||||
component.ngOnChanges({
|
||||
node: new SimpleChange(undefined, node, false)
|
||||
});
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(2);
|
||||
expect(tagElements[0].nativeElement.textContent).toBe(tagPaging.list.entries[0].entry.tag);
|
||||
expect(tagElements[1].nativeElement.textContent).toBe(tagPaging.list.entries[1].entry.tag);
|
||||
expect(tagService.getTagsByNodeId).toHaveBeenCalledWith(node.id);
|
||||
});
|
||||
|
||||
it('should not render tags after loading tags in ngOnChanges if displayTags is false', () => {
|
||||
component.displayTags = false;
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
|
||||
component.ngOnChanges({
|
||||
node: new SimpleChange(undefined, node, false)
|
||||
});
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(0);
|
||||
expect(tagService.getTagsByNodeId).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not render tags after loading tags in ngOnChanges if node is not changed', () => {
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(0);
|
||||
expect(tagService.getTagsByNodeId).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not render tags after loading tags in ngOnChanges if node is changed first time', () => {
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
|
||||
component.ngOnChanges({
|
||||
node: new SimpleChange(undefined, node, true)
|
||||
});
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(0);
|
||||
expect(tagService.getTagsByNodeId).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render tags after loading tags after clicking on Cancel button', fakeAsync(() => {
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
TestBed.inject(CardViewContentUpdateService).itemUpdated$.next({
|
||||
changed: {}
|
||||
} as UpdateNotification);
|
||||
tick(500);
|
||||
fixture.detectChanges();
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
|
||||
clickOnCancel();
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
const tagElements = findTagElements();
|
||||
expect(tagElements).toHaveSize(2);
|
||||
expect(tagElements[0].nativeElement.textContent).toBe(tagPaging.list.entries[0].entry.tag);
|
||||
expect(tagElements[1].nativeElement.textContent).toBe(tagPaging.list.entries[1].entry.tag);
|
||||
expect(tagService.getTagsByNodeId).toHaveBeenCalledOnceWith(node.id);
|
||||
}));
|
||||
|
||||
it('should be hidden when editable is true', () => {
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
expect(findTagElements()).toHaveSize(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tags creator', () => {
|
||||
let tagsCreator: TagsCreatorComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
component.editable = true;
|
||||
component.displayTags = true;
|
||||
fixture.detectChanges();
|
||||
tagsCreator = findTagsCreator();
|
||||
});
|
||||
|
||||
it('should have assigned false to tagNameControlVisible initially', () => {
|
||||
expect(tagsCreator.tagNameControlVisible).toBeFalse();
|
||||
});
|
||||
|
||||
it('should hide showing tag input button after emitting tagNameControlVisibleChange event with true', () => {
|
||||
tagsCreator.tagNameControlVisibleChange.emit(true);
|
||||
fixture.detectChanges();
|
||||
expect(findShowingTagInputButton().hasAttribute('hidden')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should show showing tag input button after emitting tagNameControlVisibleChange event with false', fakeAsync(() => {
|
||||
tagsCreator.tagNameControlVisibleChange.emit(true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
tagsCreator.tagNameControlVisibleChange.emit(false);
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
expect(findShowingTagInputButton().hasAttribute('hidden')).toBeFalse();
|
||||
}));
|
||||
|
||||
it('should have assigned correct mode', () => {
|
||||
expect(tagsCreator.mode).toBe(TagsCreatorMode.CREATE_AND_ASSIGN);
|
||||
});
|
||||
|
||||
it('should enable cancel button after emitting tagsChange event', () => {
|
||||
tagsCreator.tagsChange.emit(['New tag 1', 'New tag 2', 'New tag 3']);
|
||||
fixture.detectChanges();
|
||||
expect(findCancelButton().disabled).toBeFalse();
|
||||
});
|
||||
|
||||
it('should enable save button after emitting tagsChange event', () => {
|
||||
tagsCreator.tagsChange.emit(['New tag 1', 'New tag 2', 'New tag 3']);
|
||||
fixture.detectChanges();
|
||||
expect(findSaveButton().disabled).toBeFalse();
|
||||
});
|
||||
|
||||
it('should have assigned false to disabledTagsRemoving', () => {
|
||||
expect(tagsCreator.disabledTagsRemoving).toBeFalse();
|
||||
});
|
||||
|
||||
it('should have assigned true to disabledTagsRemoving after clicking on update button', () => {
|
||||
tagsCreator.tagsChange.emit([]);
|
||||
fixture.detectChanges();
|
||||
|
||||
clickOnSave();
|
||||
expect(tagsCreator.disabledTagsRemoving).toBeTrue();
|
||||
});
|
||||
|
||||
it('should have assigned false to disabledTagsRemoving if forkJoin fails', fakeAsync( () => {
|
||||
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
|
||||
const expectedNode = { ...node, name: 'some-modified-value' };
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
|
||||
const tagPaging = mockTagPaging();
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
component.ngOnInit();
|
||||
spyOn(tagService, 'removeTag').and.returnValue(throwError({}));
|
||||
spyOn(tagService, 'assignTagsToNode').and.returnValue(EMPTY);
|
||||
const tagName1 = tagPaging.list.entries[0].entry.tag;
|
||||
const tagName2 = 'New tag 3';
|
||||
|
||||
updateService.update(property, 'updated-value');
|
||||
tick(600);
|
||||
|
||||
fixture.detectChanges();
|
||||
tagsCreator.tagsChange.emit([tagName1, tagName2]);
|
||||
clickOnSave();
|
||||
|
||||
expect(tagsCreator.disabledTagsRemoving).toBeFalse();
|
||||
}));
|
||||
|
||||
it('should have assigned false to tagNameControlVisible after clicking on update button', () => {
|
||||
tagsCreator.tagNameControlVisibleChange.emit(true);
|
||||
tagsCreator.tagsChange.emit([]);
|
||||
fixture.detectChanges();
|
||||
|
||||
clickOnSave();
|
||||
expect(tagsCreator.tagNameControlVisible).toBeFalse();
|
||||
});
|
||||
|
||||
describe('Setting tags', () => {
|
||||
let tagPaging: TagPaging;
|
||||
|
||||
beforeEach(() => {
|
||||
tagPaging = mockTagPaging();
|
||||
spyOn(tagService, 'getTagsByNodeId').and.returnValue(of(tagPaging));
|
||||
});
|
||||
|
||||
it('should assign correct tags after ngOnInit', () => {
|
||||
component.ngOnInit();
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(tagsCreator.tags).toEqual([tagPaging.list.entries[0].entry.tag, tagPaging.list.entries[1].entry.tag]);
|
||||
expect(tagService.getTagsByNodeId).toHaveBeenCalledWith(node.id);
|
||||
});
|
||||
|
||||
it('should assign correct tags after ngOnChanges', () => {
|
||||
component.ngOnInit();
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(tagsCreator.tags).toEqual([tagPaging.list.entries[0].entry.tag, tagPaging.list.entries[1].entry.tag]);
|
||||
expect(tagService.getTagsByNodeId).toHaveBeenCalledWith(node.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show tags creator if editable is true and displayTags is true', () => {
|
||||
component.editable = true;
|
||||
component.displayTags = true;
|
||||
fixture.detectChanges();
|
||||
expect(findTagsCreator()).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not show tags creator if editable is true and displayTags is false', () => {
|
||||
component.editable = true;
|
||||
component.displayTags = false;
|
||||
fixture.detectChanges();
|
||||
expect(findTagsCreator()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
const queryDom = (fixture: ComponentFixture<ContentMetadataComponent>, properties: string = 'properties') =>
|
||||
|
||||
+8
-98
@@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { Node, TagBody, TagEntry, TagPaging } from '@alfresco/js-api';
|
||||
import { Observable, Subject, of, zip, forkJoin } from 'rxjs';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
import { Observable, Subject, of, zip } from 'rxjs';
|
||||
import {
|
||||
CardViewItem,
|
||||
LogService,
|
||||
@@ -31,8 +31,6 @@ import { CardViewGroup, PresetConfig } from '../../interfaces/content-metadata.i
|
||||
import { takeUntil, debounceTime, catchError, map } from 'rxjs/operators';
|
||||
import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service';
|
||||
import { NodesApiService } from '../../../common/services/nodes-api.service';
|
||||
import { TagsCreatorMode } from '../../../tag/tags-creator/tags-creator-mode';
|
||||
import { TagService } from '../../../tag/services/tag.service';
|
||||
|
||||
const DEFAULT_SEPARATOR = ', ';
|
||||
|
||||
@@ -52,14 +50,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
|
||||
/** Toggles whether the edit button should be shown */
|
||||
@Input()
|
||||
set editable(editable: boolean) {
|
||||
this._editable = editable;
|
||||
this._assignedTags = [...this.tags];
|
||||
}
|
||||
|
||||
get editable(): boolean {
|
||||
return this._editable;
|
||||
}
|
||||
editable: boolean = false;
|
||||
|
||||
/** Toggles whether to display empty values in the card view */
|
||||
@Input()
|
||||
@@ -95,25 +86,13 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
@Input()
|
||||
useChipsForMultiValueProperty: boolean = true;
|
||||
|
||||
/** True if tags should be displayed, false otherwise */
|
||||
@Input()
|
||||
displayTags = false;
|
||||
|
||||
multiValueSeparator: string;
|
||||
basicProperties$: Observable<CardViewItem[]>;
|
||||
groupedProperties$: Observable<CardViewGroup[]>;
|
||||
|
||||
changedProperties = {};
|
||||
hasMetadataChanged = false;
|
||||
tagNameControlVisible = false;
|
||||
|
||||
private _assignedTags: string[] = [];
|
||||
private assignedTagsEntries: TagEntry[] = [];
|
||||
private _editable = false;
|
||||
private _tagsCreatorMode = TagsCreatorMode.CREATE_AND_ASSIGN;
|
||||
private _tags: string[] = [];
|
||||
private targetProperty: CardViewBaseItemModel;
|
||||
private _saving = false;
|
||||
|
||||
constructor(
|
||||
private contentMetadataService: ContentMetadataService,
|
||||
@@ -121,8 +100,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
private nodesApiService: NodesApiService,
|
||||
private logService: LogService,
|
||||
private translationService: TranslationService,
|
||||
private appConfig: AppConfigService,
|
||||
private tagService: TagService
|
||||
private appConfig: AppConfigService
|
||||
) {
|
||||
this.copyToClipboardAction = this.appConfig.get<boolean>('content-metadata.copy-to-clipboard-action');
|
||||
this.multiValueSeparator = this.appConfig.get<string>('content-metadata.multi-value-pipe-separator') || DEFAULT_SEPARATOR;
|
||||
@@ -150,22 +128,6 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
this.loadProperties(this.node);
|
||||
}
|
||||
|
||||
get assignedTags(): string[] {
|
||||
return this._assignedTags;
|
||||
}
|
||||
|
||||
get tags(): string[] {
|
||||
return this._tags;
|
||||
}
|
||||
|
||||
get tagsCreatorMode(): TagsCreatorMode {
|
||||
return this._tagsCreatorMode;
|
||||
}
|
||||
|
||||
get saving(): boolean {
|
||||
return this._saving;
|
||||
}
|
||||
|
||||
protected handleUpdateError(error: Error) {
|
||||
this.logService.error(error);
|
||||
|
||||
@@ -198,9 +160,6 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
if (node) {
|
||||
this.basicProperties$ = this.getProperties(node);
|
||||
this.groupedProperties$ = this.contentMetadataService.getGroupedProperties(node, this.preset);
|
||||
if (this.displayTags) {
|
||||
this.loadTagsForNode(node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,12 +186,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after clicking save button. It confirms all changes done for metadata. Before clicking on that button they are not saved.
|
||||
*/
|
||||
saveChanges() {
|
||||
this._saving = true;
|
||||
this.tagNameControlVisible = false;
|
||||
if (this.hasContentTypeChanged(this.changedProperties)) {
|
||||
this.contentMetadataService.openConfirmDialog(this.changedProperties).subscribe(() => {
|
||||
this.updateNode();
|
||||
@@ -242,39 +196,22 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all tags which should be assigned to node. Please note that they are just in "register" state and are not yet saved
|
||||
* until button for saving data is clicked. Calling that function causes that save button is enabled.
|
||||
* @param tags array of tags to register, they are not saved yet until we click save button.
|
||||
*/
|
||||
storeTagsToAssign(tags: string[]) {
|
||||
this._tags = tags;
|
||||
this.hasMetadataChanged = true;
|
||||
}
|
||||
|
||||
private updateNode() {
|
||||
forkJoin({
|
||||
updatedNode: this.nodesApiService.updateNode(this.node.id, this.changedProperties),
|
||||
...(this.displayTags ? this.saveTags() : {})
|
||||
}).pipe(
|
||||
this.nodesApiService.updateNode(this.node.id, this.changedProperties).pipe(
|
||||
catchError((err) => {
|
||||
this.cardViewContentUpdateService.updateElement(this.targetProperty);
|
||||
this.handleUpdateError(err);
|
||||
return of(null);
|
||||
}))
|
||||
.subscribe((result) => {
|
||||
if (result) {
|
||||
.subscribe((updatedNode) => {
|
||||
if (updatedNode) {
|
||||
if (this.hasContentTypeChanged(this.changedProperties)) {
|
||||
this.cardViewContentUpdateService.updateNodeAspect(this.node);
|
||||
}
|
||||
this.revertChanges();
|
||||
Object.assign(this.node, result.updatedNode);
|
||||
Object.assign(this.node, updatedNode);
|
||||
this.nodesApiService.nodeUpdated.next(this.node);
|
||||
if (Object.keys(result).length > 1 && this.displayTags) {
|
||||
this.loadTagsForNode(this.node.id);
|
||||
}
|
||||
}
|
||||
this._saving = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -320,31 +257,4 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
private isEmpty(value: any): boolean {
|
||||
return value === undefined || value === null || value === '';
|
||||
}
|
||||
|
||||
private loadTagsForNode(id: string) {
|
||||
this.tagService.getTagsByNodeId(id).subscribe((tagPaging) => {
|
||||
this.assignedTagsEntries = tagPaging.list.entries;
|
||||
this._tags = tagPaging.list.entries.map((tagEntry) => tagEntry.entry.tag);
|
||||
this._assignedTags = [...this._tags];
|
||||
});
|
||||
}
|
||||
|
||||
private saveTags(): { [key: string]: Observable<TagPaging | TagEntry | void> } {
|
||||
const observables: { [key: string]: Observable<TagPaging | TagEntry | void> } = {};
|
||||
if (this.tags) {
|
||||
this.assignedTagsEntries.forEach((tagEntry) => {
|
||||
if (!this.tags.some((tag) => tagEntry.entry.tag === tag)) {
|
||||
observables[`${tagEntry.entry.id}Removing`] = this.tagService.removeTag(this.node.id, tagEntry.entry.id);
|
||||
}
|
||||
});
|
||||
if (this.tags.length) {
|
||||
observables.tagsAssigning = this.tagService.assignTagsToNode(this.node.id, this.tags.map((tag) => {
|
||||
const tagBody = new TagBody();
|
||||
tagBody.tag = tag;
|
||||
return tagBody;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return observables;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,13 @@ import { MaterialModule } from '../material.module';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
import { ContentMetadataComponent } from './components/content-metadata/content-metadata.component';
|
||||
import { ContentMetadataCardComponent } from './components/content-metadata-card/content-metadata-card.component';
|
||||
import { TagModule } from '../tag/tag.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MaterialModule,
|
||||
FlexLayoutModule,
|
||||
CoreModule,
|
||||
TagModule
|
||||
CoreModule
|
||||
],
|
||||
exports: [
|
||||
ContentMetadataComponent,
|
||||
|
||||
+2
-37
@@ -27,7 +27,7 @@ import {
|
||||
DataTableModule,
|
||||
ObjectDataTableAdapter,
|
||||
ShowHeaderMode,
|
||||
ThumbnailService, AppConfigService
|
||||
ThumbnailService
|
||||
} from '@alfresco/adf-core';
|
||||
import { ContentService } from '../../common/services/content.service';
|
||||
|
||||
@@ -62,12 +62,6 @@ import { ShareDataRow } from '../data/share-data-row.model';
|
||||
import { DocumentLoaderNode } from '../models/document-folder.model';
|
||||
import { matIconRegistryMock } from '../../testing/mat-icon-registry-mock';
|
||||
import { domSanitizerMock } from '../../testing/dom-sanitizer-mock';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { FileAutoDownloadComponent } from './file-auto-download/file-auto-download.component';
|
||||
|
||||
const mockDialog = {
|
||||
open: jasmine.createSpy('open')
|
||||
};
|
||||
|
||||
describe('DocumentList', () => {
|
||||
|
||||
@@ -77,7 +71,6 @@ describe('DocumentList', () => {
|
||||
let customResourcesService: CustomResourcesService;
|
||||
let thumbnailService: ThumbnailService;
|
||||
let contentService: ContentService;
|
||||
let appConfigService: AppConfigService;
|
||||
let fixture: ComponentFixture<DocumentListComponent>;
|
||||
let element: HTMLElement;
|
||||
let eventMock: any;
|
||||
@@ -91,10 +84,7 @@ describe('DocumentList', () => {
|
||||
TranslateModule.forRoot(),
|
||||
ContentTestingModule
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
providers: [
|
||||
{ provide: MatDialog, useValue: mockDialog }
|
||||
]
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -112,7 +102,6 @@ describe('DocumentList', () => {
|
||||
customResourcesService = TestBed.inject(CustomResourcesService);
|
||||
thumbnailService = TestBed.inject(ThumbnailService);
|
||||
contentService = TestBed.inject(ContentService);
|
||||
appConfigService = TestBed.inject(AppConfigService);
|
||||
|
||||
spyFolder = spyOn(documentListService, 'getFolder').and.returnValue(of({ list: {} }));
|
||||
spyFolderNode = spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry({ entry: {} })));
|
||||
@@ -1578,30 +1567,6 @@ describe('DocumentList', () => {
|
||||
}), undefined);
|
||||
});
|
||||
|
||||
it('should display fileAutoDownload dialog if node size exceeds appConfig.viewer.fileAutoDownloadSizeThresholdInMB', async () => {
|
||||
appConfigService.config = {
|
||||
...appConfigService.config,
|
||||
'viewer': {
|
||||
'enableFileAutoDownload': true,
|
||||
'fileAutoDownloadSizeThresholdInMB': 10
|
||||
}
|
||||
};
|
||||
documentList.navigationMode = DocumentListComponent.SINGLE_CLICK_NAVIGATION;
|
||||
const node = { entry: {
|
||||
...mockNode1,
|
||||
content: {
|
||||
...mockNode1.content,
|
||||
sizeInBytes: 104857600
|
||||
}
|
||||
} };
|
||||
documentList.onNodeClick(node);
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(mockDialog.open).toHaveBeenCalledWith(FileAutoDownloadComponent, { disableClose: true, data: node });
|
||||
});
|
||||
|
||||
describe('Preselect nodes', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -66,10 +66,6 @@ import { LockService } from '../services/lock.service';
|
||||
import { DocumentLoaderNode } from '../models/document-folder.model';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ADF_DOCUMENT_PARENT_COMPONENT } from './document-list.token';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { FileAutoDownloadComponent } from './file-auto-download/file-auto-download.component';
|
||||
|
||||
const BYTES_TO_MB_CONVERSION_VALUE = 1048576;
|
||||
|
||||
@Component({
|
||||
selector: 'adf-document-list',
|
||||
@@ -371,8 +367,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
private alfrescoApiService: AlfrescoApiService,
|
||||
private nodeService: NodesApiService,
|
||||
private dataTableService: DataTableService,
|
||||
private lockService: LockService,
|
||||
private dialog: MatDialog) {
|
||||
private lockService: LockService) {
|
||||
|
||||
this.nodeService.nodeUpdated.subscribe((node) => {
|
||||
this.dataTableService.rowUpdate.next({id: node.id, obj: {entry: node}});
|
||||
@@ -763,16 +758,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
|
||||
onPreviewFile(node: NodeEntry) {
|
||||
if (node) {
|
||||
const sizeInMB = node.entry?.content?.sizeInBytes / BYTES_TO_MB_CONVERSION_VALUE;
|
||||
|
||||
const fileAutoDownloadFlag: boolean = this.appConfig.get('viewer.enableFileAutoDownload', true);
|
||||
const sizeThreshold: number = this.appConfig.get('viewer.fileAutoDownloadSizeThresholdInMB', 15);
|
||||
|
||||
if (fileAutoDownloadFlag && sizeInMB && sizeInMB > sizeThreshold) {
|
||||
this.dialog.open(FileAutoDownloadComponent, { disableClose: true, data: node });
|
||||
} else {
|
||||
this.preview.emit(new NodeEntityEvent(node));
|
||||
}
|
||||
this.preview.emit(new NodeEntityEvent(node));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
<div mat-dialog-title>
|
||||
<h3>{{ 'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.HEADER' | translate }}</h3>
|
||||
</div>
|
||||
<mat-dialog-content>
|
||||
{{ 'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.LABEL' | translate }}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button
|
||||
mat-button
|
||||
mat-dialog-close
|
||||
id="cancelButton"
|
||||
[attr.aria-label]="'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.ACTIONS.CANCEL' | translate">
|
||||
{{ 'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.ACTIONS.CANCEL' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
mat-dialog-close
|
||||
id="downloadButton"
|
||||
color="primary"
|
||||
[attr.aria-label]="'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.ACTIONS.DOWNLOAD' | translate"
|
||||
[adfNodeDownload]="node">
|
||||
{{ 'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.ACTIONS.DOWNLOAD' | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FileAutoDownloadComponent } from './file-auto-download.component';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
const mockDialog = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
describe('FileAutoDownloadComponent', () => {
|
||||
let matDialogRef: MatDialogRef<FileAutoDownloadComponent>;
|
||||
let fixture: ComponentFixture<FileAutoDownloadComponent>;
|
||||
|
||||
const getButton = (buttonId: string) => {
|
||||
return fixture.debugElement.query(By.css(buttonId)).nativeElement;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FileAutoDownloadComponent],
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
CoreTestingModule
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: mockDialog },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: null }
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(FileAutoDownloadComponent);
|
||||
matDialogRef = TestBed.inject(MatDialogRef);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should emit FileAutoDownloadActionsEnum.CANCEL and close dialog when clicking on the cancel button', async () => {
|
||||
const waitButton = getButton('#cancelButton');
|
||||
waitButton.dispatchEvent(new Event('click'));
|
||||
|
||||
await fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(matDialogRef.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit FileAutoDownloadActionsEnum.DOWNLOAD and close dialog when clicking on the wait button', async () => {
|
||||
const waitButton = getButton('#downloadButton');
|
||||
waitButton.dispatchEvent(new Event('click'));
|
||||
|
||||
await fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(matDialogRef.close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-file-auto-download',
|
||||
templateUrl: './file-auto-download.component.html'
|
||||
})
|
||||
export class FileAutoDownloadComponent {
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public node: NodeEntry) {}
|
||||
}
|
||||
@@ -35,8 +35,6 @@ import { LibraryRoleColumnComponent } from './components/library-role-column/lib
|
||||
import { LibraryNameColumnComponent } from './components/library-name-column/library-name-column.component';
|
||||
import { NameColumnComponent } from './components/name-column/name-column.component';
|
||||
import { FilterHeaderComponent } from './components/filter-header/filter-header.component';
|
||||
import { FileAutoDownloadComponent } from './components/file-auto-download/file-auto-download.component';
|
||||
import { ContentDirectiveModule } from '../directives/content-directive.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -47,8 +45,7 @@ import { ContentDirectiveModule } from '../directives/content-directive.module';
|
||||
MaterialModule,
|
||||
UploadModule,
|
||||
EditJsonDialogModule,
|
||||
SearchModule,
|
||||
ContentDirectiveModule
|
||||
SearchModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent,
|
||||
@@ -59,8 +56,7 @@ import { ContentDirectiveModule } from '../directives/content-directive.module';
|
||||
NameColumnComponent,
|
||||
ContentActionComponent,
|
||||
ContentActionListComponent,
|
||||
FilterHeaderComponent,
|
||||
FileAutoDownloadComponent
|
||||
FilterHeaderComponent
|
||||
],
|
||||
exports: [
|
||||
DocumentListComponent,
|
||||
|
||||
@@ -25,7 +25,6 @@ export * from './components/library-status-column/library-status-column.componen
|
||||
export * from './components/name-column/name-column.component';
|
||||
export * from './components/filter-header/filter-header.component';
|
||||
export * from './components/trashcan-name-column/trashcan-name-column.component';
|
||||
export * from './components/file-auto-download/file-auto-download.component';
|
||||
|
||||
// data
|
||||
export * from './data/share-datatable-adapter';
|
||||
|
||||
@@ -73,15 +73,7 @@
|
||||
"REMOVE": "Remove",
|
||||
"DOWNLOAD": "Download"
|
||||
},
|
||||
"LOADER_LABEL": "Document list loader",
|
||||
"FILE_AUTO_DOWNLOAD_DIALOG": {
|
||||
"HEADER": "Preview loading unresponsive",
|
||||
"LABEL": "Download to view this document, or cancel to continue without preview.",
|
||||
"ACTIONS": {
|
||||
"CANCEL": "Cancel",
|
||||
"DOWNLOAD": "Download"
|
||||
}
|
||||
}
|
||||
"LOADER_LABEL": "Document list loader"
|
||||
},
|
||||
"ALFRESCO_DOCUMENT_LIST": {
|
||||
"BUTTON": {
|
||||
@@ -140,51 +132,6 @@
|
||||
},
|
||||
"BUTTON": {
|
||||
"ADD": "Add Tag"
|
||||
},
|
||||
"TAGS_CREATOR": {
|
||||
"EXISTING_TAGS": "Existing tags:",
|
||||
"EXISTING_TAGS_SELECTION": "Select an existing tag:",
|
||||
"NO_TAGS_CREATED": "No Tags Created",
|
||||
"NO_EXISTING_TAGS": "No Existing Tags",
|
||||
"TITLE": "Create Tags",
|
||||
"CREATE_TAG": "Create: {{tag}}",
|
||||
"NAME": "Name",
|
||||
"ERRORS": {
|
||||
"EXISTING_TAG": "Tag already exists",
|
||||
"ALREADY_ADDED_TAG": "Tag is already added",
|
||||
"EMPTY_TAG": "Tag name can't contain only spaces",
|
||||
"REQUIRED": "Tag name is required",
|
||||
"FETCH_TAGS": "Error while fetching the tags",
|
||||
"CREATE_TAGS": "Error while creating the tags"
|
||||
},
|
||||
"TOOLTIPS": {
|
||||
"DELETE_TAG": "Delete tag",
|
||||
"HIDE_INPUT": "Hide input"
|
||||
},
|
||||
"TAGS_LOADING": "Tags loading",
|
||||
"CREATE_TAGS_SUCCESS": "Tags created successfully"
|
||||
}
|
||||
},
|
||||
"CATEGORIES_MANAGEMENT": {
|
||||
"CATEGORIES_TITLE": "Categories",
|
||||
"NO_CATEGORIES_CREATED": "No Categories created",
|
||||
"NO_CATEGORIES_ASSIGNED": "No Categories assigned",
|
||||
"ASSIGN_CATEGORIES": "Assign Categories",
|
||||
"UNASSIGN_CATEGORY": "Unassign Category",
|
||||
"DELETE_CATEGORY": "Delete Category",
|
||||
"EXISTING_CATEGORIES": "Existing Categories",
|
||||
"GENERIC_CREATE": "Create: {{name}}",
|
||||
"NAME": "Category name",
|
||||
"LOADING": "Loading",
|
||||
"HIDE_INPUT": "Hide input",
|
||||
"ERRORS": {
|
||||
"NOT_FOUND": "Categories not found",
|
||||
"REQUIRED": "Category name is required",
|
||||
"EMPTY_CATEGORY": "Category name can't contain only spaces",
|
||||
"ALREADY_EXISTS": "Category already exists",
|
||||
"ALREADY_ADDED_CATEGORY": "Category is already added",
|
||||
"CREATE_CATEGORIES": "Error while creating categories",
|
||||
"EXISTING_CATEGORIES": "Some categories already exist"
|
||||
}
|
||||
},
|
||||
"ADF_FILE_UPLOAD": {
|
||||
@@ -412,9 +359,7 @@
|
||||
"CREATED_DATE": "Created Date",
|
||||
"MODIFIER": "Modifier",
|
||||
"MODIFIED_DATE": "Modified Date",
|
||||
"CONTENT_TYPE": "Content Type",
|
||||
"TAGS": "Tags",
|
||||
"ADD_TAG_TOOLTIP": "Add tag"
|
||||
"CONTENT_TYPE": "Content Type"
|
||||
},
|
||||
"CONTENT_TYPE": {
|
||||
"DIALOG" :{
|
||||
|
||||
@@ -22,7 +22,3 @@ export * from './tag-node-list.component';
|
||||
export * from './services/tag.service';
|
||||
|
||||
export * from './tag.module';
|
||||
|
||||
export * from './tags-creator/tags-creator-mode';
|
||||
export * from './tags-creator/tags-creator.component';
|
||||
|
||||
|
||||
@@ -345,50 +345,5 @@ describe('TagService', () => {
|
||||
tick();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('assignTagsToNode', () => {
|
||||
let singleResult: TagEntry;
|
||||
let tags: TagBody[];
|
||||
|
||||
const nodeId = 'some node id';
|
||||
|
||||
beforeEach(() => {
|
||||
singleResult = new TagEntry();
|
||||
singleResult.entry = new Tag();
|
||||
singleResult.entry.tag = 'some name';
|
||||
const tag = new TagBody();
|
||||
tag.tag = 'some name';
|
||||
tags = [tag];
|
||||
});
|
||||
|
||||
it('should call assignTagsToNode on TagsApi with correct parameters', () => {
|
||||
spyOn(service.tagsApi, 'assignTagsToNode').and.returnValue(Promise.resolve(singleResult));
|
||||
|
||||
service.assignTagsToNode(nodeId, tags);
|
||||
expect(service.tagsApi.assignTagsToNode).toHaveBeenCalledWith(nodeId, tags);
|
||||
});
|
||||
|
||||
it('should return observable which emits paging object for tags', fakeAsync(() => {
|
||||
const pagingResult = mockTagPaging();
|
||||
const tag2 = new TagBody();
|
||||
tag2.tag = 'some other tag';
|
||||
tags.push(tag2);
|
||||
spyOn(service.tagsApi, 'assignTagsToNode').and.returnValue(Promise.resolve(pagingResult));
|
||||
|
||||
service.assignTagsToNode(nodeId, tags).subscribe((tagsResult) => {
|
||||
expect(tagsResult).toEqual(pagingResult);
|
||||
});
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should return observable which emits single tag', fakeAsync(() => {
|
||||
spyOn(service.tagsApi, 'assignTagsToNode').and.returnValue(Promise.resolve(singleResult));
|
||||
|
||||
service.assignTagsToNode(nodeId, tags).subscribe((tagsResult) => {
|
||||
expect(tagsResult).toEqual(singleResult);
|
||||
});
|
||||
tick();
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ export class TagService {
|
||||
* @param tag Name of the tag to remove
|
||||
* @returns Null object when the operation completes
|
||||
*/
|
||||
removeTag(nodeId: string, tag: string): Observable<void> {
|
||||
removeTag(nodeId: string, tag: string): Observable<any> {
|
||||
const observableRemove = from(this.tagsApi.deleteTagFromNode(nodeId, tag));
|
||||
|
||||
observableRemove.subscribe((data) => {
|
||||
@@ -191,20 +191,6 @@ export class TagService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign tags to node. If tag is new then tag is also created additionally, if tag already exists then it is just assigned.
|
||||
*
|
||||
* @param nodeId Id of node to which tags should be assigned.
|
||||
* @param tags List of tags to create and assign or just assign if they already exist.
|
||||
*
|
||||
* @return Just linked tags to node or single tag if linked only one tag.
|
||||
*/
|
||||
assignTagsToNode(nodeId: string, tags: TagBody[]): Observable<TagPaging | TagEntry> {
|
||||
return from(this.tagsApi.assignTagsToNode(nodeId, tags)).pipe(
|
||||
tap((data) => this.refresh.emit(data))
|
||||
);
|
||||
}
|
||||
|
||||
private handleError(error: any) {
|
||||
this.logService.error(error);
|
||||
return throwError(error || 'Server error');
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('TagActionsComponent', () => {
|
||||
it('Tag list click on delete button should delete the tag', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
|
||||
spyOn(tagService, 'removeTag').and.returnValue(of(undefined));
|
||||
spyOn(tagService, 'removeTag').and.returnValue(of(true));
|
||||
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
<button mat-button
|
||||
[hidden]="!limitTagsDisplayed"
|
||||
[style.left.px]="viewMoreButtonLeftOffset"
|
||||
*ngIf="limitTagsDisplayed"
|
||||
class="adf-view-more-button"
|
||||
[class.adf-hidden-btn]="!calculationsDone"
|
||||
(click)="displayAllTags($event)"
|
||||
|
||||
@@ -4,22 +4,13 @@
|
||||
flex-direction: row;
|
||||
width: inherit;
|
||||
|
||||
&.adf-flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.adf-view-more-button {
|
||||
margin: 4px;
|
||||
color: var(--adf-theme-foreground-text-color-054);
|
||||
position: absolute;
|
||||
|
||||
&[hidden] {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.adf-flex-column {
|
||||
flex-direction: column;
|
||||
|
||||
.adf-view-more-button {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-full-width {
|
||||
|
||||
@@ -44,19 +44,15 @@ describe('TagNodeList', () => {
|
||||
},
|
||||
{
|
||||
entry: {tag: 'test3', id: 'fb4213c0-729d-466c-9a6c-ee2e937273bf'}
|
||||
},
|
||||
{
|
||||
entry: {tag: 'test4', id: 'as4213c0-729d-466c-9a6c-ee2e937273as'}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let component: TagNodeListComponent;
|
||||
let component: any;
|
||||
let fixture: ComponentFixture<TagNodeListComponent>;
|
||||
let element: HTMLElement;
|
||||
let tagService: TagService;
|
||||
let resizeCallback: ResizeObserverCallback;
|
||||
|
||||
function findViewMoreButton(): HTMLButtonElement {
|
||||
return element.querySelector('.adf-view-more-button');
|
||||
@@ -74,7 +70,6 @@ describe('TagNodeList', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const resizeObserverSpy = spyOn(window, 'ResizeObserver').and.callThrough();
|
||||
fixture = TestBed.createComponent(TagNodeListComponent);
|
||||
|
||||
tagService = TestBed.inject(TagService);
|
||||
@@ -84,7 +79,6 @@ describe('TagNodeList', () => {
|
||||
component = fixture.componentInstance;
|
||||
component.nodeId = 'fake-node-id';
|
||||
fixture.detectChanges();
|
||||
resizeCallback = resizeObserverSpy.calls.mostRecent().args[0];
|
||||
});
|
||||
|
||||
describe('Rendering tests', () => {
|
||||
@@ -104,7 +98,7 @@ describe('TagNodeList', () => {
|
||||
});
|
||||
|
||||
it('Tag list click on delete button should delete the tag', async () => {
|
||||
spyOn(tagService, 'removeTag').and.returnValue(of(undefined));
|
||||
spyOn(tagService, 'removeTag').and.returnValue(of(true));
|
||||
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
@@ -143,29 +137,24 @@ describe('TagNodeList', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(findViewMoreButton().hidden).toBeTrue();
|
||||
expect(findTagChips()).toHaveSize(4);
|
||||
expect(findViewMoreButton()).toBeNull();
|
||||
expect(findTagChips().length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Limit tags display', () => {
|
||||
let initialEntries: TagEntry[];
|
||||
|
||||
async function renderTags(entries?: TagEntry[]): Promise<any> {
|
||||
dataTag.list.entries = entries || initialEntries;
|
||||
if (entries) {
|
||||
dataTag.list.entries = entries;
|
||||
}
|
||||
component.tagsEntries = dataTag.list.entries;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
initialEntries = dataTag.list.entries;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
component.limitTagsDisplayed = true;
|
||||
component.ngOnInit();
|
||||
element.style.maxWidth = '309px';
|
||||
element.style.maxWidth = '200px';
|
||||
});
|
||||
|
||||
it('should render view more button when limiting is enabled', async () => {
|
||||
@@ -173,8 +162,8 @@ describe('TagNodeList', () => {
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(findViewMoreButton().hidden).toBeFalse();
|
||||
expect(findTagChips()).toHaveSize(component.tagsEntries.length);
|
||||
expect(findViewMoreButton()).not.toBeNull();
|
||||
expect(findTagChips().length).toBe(component.tagsEntries.length);
|
||||
});
|
||||
|
||||
it('should not render view more button when limiting is enabled and all tags fits into container', async () => {
|
||||
@@ -185,8 +174,8 @@ describe('TagNodeList', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(findViewMoreButton().hidden).toBeTrue();
|
||||
expect(findTagChips()).toHaveSize(4);
|
||||
expect(findViewMoreButton()).toBeNull();
|
||||
expect(findTagChips().length).toBe(3);
|
||||
});
|
||||
|
||||
it('should display all tags when view more button is clicked', async () => {
|
||||
@@ -201,8 +190,8 @@ describe('TagNodeList', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
viewMoreButton = findViewMoreButton();
|
||||
expect(viewMoreButton.hidden).toBeTrue();
|
||||
expect(findTagChips()).toHaveSize(4);
|
||||
expect(viewMoreButton).toBeNull();
|
||||
expect(findTagChips().length).toBe(3);
|
||||
});
|
||||
|
||||
it('should not render view more button when tag takes more than one line and there are no more tags', async () => {
|
||||
@@ -215,8 +204,8 @@ describe('TagNodeList', () => {
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(findViewMoreButton().hidden).toBeTrue();
|
||||
expect(findTagChips()).toHaveSize(component.tagsEntries.length);
|
||||
expect(findViewMoreButton()).toBeNull();
|
||||
expect(findTagChips().length).toBe(component.tagsEntries.length);
|
||||
});
|
||||
|
||||
it('should render view more button when tag takes more than one line and there are more tags', async () => {
|
||||
@@ -234,53 +223,8 @@ describe('TagNodeList', () => {
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const viewMoreButton = findViewMoreButton();
|
||||
expect(viewMoreButton.hidden).toBeFalse();
|
||||
expect(viewMoreButton.style.left).toBe('0px');
|
||||
expect(findTagChips()).toHaveSize(component.tagsEntries.length);
|
||||
});
|
||||
|
||||
it('should not render view more button when there is enough space after resizing', async () => {
|
||||
await renderTags();
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
element.style.maxWidth = '800px';
|
||||
resizeCallback([], null);
|
||||
fixture.detectChanges();
|
||||
const viewMoreButton = findViewMoreButton();
|
||||
expect(viewMoreButton.hidden).toBeTrue();
|
||||
expect(findTagChips()).toHaveSize(4);
|
||||
});
|
||||
|
||||
it('should render view more button when there is not enough space after resizing', async () => {
|
||||
await renderTags();
|
||||
element.style.maxWidth = '800px';
|
||||
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
element.style.maxWidth = '309px';
|
||||
resizeCallback([], null);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(findViewMoreButton().hidden).toBeFalse();
|
||||
expect(findTagChips()).toHaveSize(component.tagsEntries.length);
|
||||
});
|
||||
|
||||
it('should not render view more button again resizing when there is not enough space if user requested to see all tags', async () => {
|
||||
await renderTags();
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const viewMoreButton = findViewMoreButton();
|
||||
viewMoreButton.click();
|
||||
fixture.detectChanges();
|
||||
element.style.maxWidth = '309px';
|
||||
resizeCallback([], null);
|
||||
fixture.detectChanges();
|
||||
expect(viewMoreButton.hidden).toBeTrue();
|
||||
expect(findTagChips()).toHaveSize(4);
|
||||
expect(findViewMoreButton()).not.toBeNull();
|
||||
expect(findTagChips().length).toBe(component.tagsEntries.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,22 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
ViewEncapsulation,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
ViewChildren,
|
||||
QueryList,
|
||||
ChangeDetectorRef,
|
||||
AfterViewInit
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation, OnDestroy, OnInit, ViewChild, ElementRef, ViewChildren, QueryList } from '@angular/core';
|
||||
import { TagService } from './services/tag.service';
|
||||
import { TagEntry } from '@alfresco/js-api';
|
||||
import { Subject } from 'rxjs';
|
||||
@@ -48,7 +33,7 @@ import { MatChip } from '@angular/material/chips';
|
||||
styleUrls: ['./tag-node-list.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit, AfterViewInit {
|
||||
export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
|
||||
/* eslint no-underscore-dangle: ["error", { "allow": ["_elementRef"] }]*/
|
||||
/** The identifier of a node. */
|
||||
@Input()
|
||||
@@ -72,29 +57,19 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit, After
|
||||
calculationsDone = false;
|
||||
columnFlexDirection = false;
|
||||
undisplayedTagsCount = 0;
|
||||
viewMoreButtonLeftOffset: number;
|
||||
|
||||
/** Emitted when a tag is selected. */
|
||||
@Output()
|
||||
results = new EventEmitter();
|
||||
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
private initialLimitTagsDisplayed: boolean;
|
||||
private initialTagsEntries: TagEntry[] = [];
|
||||
private viewMoreButtonLeftOffsetBeforeFlexDirection: number;
|
||||
private requestedDisplayingAllTags = false;
|
||||
private resizeObserver = new ResizeObserver(() => {
|
||||
this.calculateTagsToDisplay();
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param tagService
|
||||
* @param changeDetectorRef
|
||||
*/
|
||||
constructor(private tagService: TagService, private changeDetectorRef: ChangeDetectorRef) {
|
||||
constructor(private tagService: TagService) {
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
@@ -102,7 +77,6 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit, After
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.initialLimitTagsDisplayed = this.limitTagsDisplayed;
|
||||
this.tagService.refresh
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(() => this.refreshTag());
|
||||
@@ -116,21 +90,15 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit, After
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.resizeObserver.observe(this.containerView.nativeElement);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
this.resizeObserver.unobserve(this.containerView.nativeElement);
|
||||
}
|
||||
|
||||
refreshTag() {
|
||||
if (this.nodeId) {
|
||||
this.tagService.getTagsByNodeId(this.nodeId).subscribe((tagPaging) => {
|
||||
this.tagsEntries = tagPaging.list.entries;
|
||||
this.initialTagsEntries = tagPaging.list.entries;
|
||||
this.results.emit(this.tagsEntries);
|
||||
});
|
||||
}
|
||||
@@ -146,39 +114,29 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit, After
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.limitTagsDisplayed = false;
|
||||
this.requestedDisplayingAllTags = true;
|
||||
this.resizeObserver.unobserve(this.containerView.nativeElement);
|
||||
this.refreshTag();
|
||||
}
|
||||
|
||||
private calculateTagsToDisplay() {
|
||||
if (!this.requestedDisplayingAllTags) {
|
||||
this.tagsEntries = this.initialTagsEntries;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.undisplayedTagsCount = 0;
|
||||
let tagsToDisplay = 1;
|
||||
const containerWidth: number = this.containerView.nativeElement.clientWidth;
|
||||
const viewMoreBtnWidth: number = this.containerView.nativeElement.children[1].offsetWidth;
|
||||
const firstTag = this.tagChips.get(0);
|
||||
const tagChipMargin = firstTag ? this.getTagChipMargin(this.tagChips.get(0)) : 0;
|
||||
const tagChipsWidth: number = this.tagChips.reduce((width, val, index) => {
|
||||
width += val._elementRef.nativeElement.offsetWidth + tagChipMargin;
|
||||
if (containerWidth - viewMoreBtnWidth > width) {
|
||||
tagsToDisplay = index + 1;
|
||||
this.viewMoreButtonLeftOffset = width;
|
||||
this.viewMoreButtonLeftOffsetBeforeFlexDirection = width;
|
||||
}
|
||||
return width;
|
||||
}, 0);
|
||||
if ((containerWidth - tagChipsWidth) <= 0) {
|
||||
this.columnFlexDirection = tagsToDisplay === 1 && (containerWidth < (this.tagChips.get(0)._elementRef.nativeElement.offsetWidth + viewMoreBtnWidth));
|
||||
this.undisplayedTagsCount = this.tagsEntries.length - tagsToDisplay;
|
||||
this.tagsEntries = this.tagsEntries.slice(0, tagsToDisplay);
|
||||
let tagsToDisplay = 1;
|
||||
const containerWidth: number = this.containerView.nativeElement.clientWidth;
|
||||
const viewMoreBtnWidth: number = this.containerView.nativeElement.children[1].offsetWidth;
|
||||
const tagChipMargin = this.getTagChipMargin(this.tagChips.get(0));
|
||||
const tagChipsWidth: number = this.tagChips.reduce((acc, val, index) => {
|
||||
if (containerWidth - viewMoreBtnWidth > acc + val._elementRef.nativeElement.offsetWidth) {
|
||||
tagsToDisplay = index + 1;
|
||||
}
|
||||
this.limitTagsDisplayed = this.undisplayedTagsCount ? this.initialLimitTagsDisplayed : false;
|
||||
this.viewMoreButtonLeftOffset = this.columnFlexDirection ? 0 : this.viewMoreButtonLeftOffsetBeforeFlexDirection;
|
||||
this.calculationsDone = true;
|
||||
return acc + val._elementRef.nativeElement.offsetWidth + tagChipMargin;
|
||||
}, 0);
|
||||
if ((containerWidth - tagChipsWidth) <= 0) {
|
||||
this.columnFlexDirection = tagsToDisplay === 1 && (containerWidth < (this.tagChips.get(0)._elementRef.nativeElement.offsetWidth + viewMoreBtnWidth));
|
||||
this.undisplayedTagsCount = this.tagsEntries.length - tagsToDisplay;
|
||||
this.tagsEntries = this.tagsEntries.slice(0, tagsToDisplay);
|
||||
}
|
||||
if (!this.undisplayedTagsCount) {
|
||||
this.limitTagsDisplayed = false;
|
||||
}
|
||||
this.calculationsDone = true;
|
||||
}
|
||||
|
||||
private getTagChipMargin(chip: MatChip): number {
|
||||
|
||||
@@ -24,13 +24,10 @@ import { TagActionsComponent } from './tag-actions.component';
|
||||
import { TagListComponent } from './tag-list.component';
|
||||
import { TagNodeListComponent } from './tag-node-list.component';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
import { TagsCreatorComponent } from './tags-creator/tags-creator.component';
|
||||
import { ContentDirectiveModule } from '../directives/content-directive.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ContentDirectiveModule,
|
||||
MaterialModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
@@ -39,14 +36,12 @@ import { ContentDirectiveModule } from '../directives/content-directive.module';
|
||||
exports: [
|
||||
TagActionsComponent,
|
||||
TagListComponent,
|
||||
TagNodeListComponent,
|
||||
TagsCreatorComponent
|
||||
TagNodeListComponent
|
||||
],
|
||||
declarations: [
|
||||
TagActionsComponent,
|
||||
TagListComponent,
|
||||
TagNodeListComponent,
|
||||
TagsCreatorComponent
|
||||
TagNodeListComponent
|
||||
]
|
||||
})
|
||||
export class TagModule {}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Available modes for tags creator.
|
||||
* Create mode allows only for creating completely new tags.
|
||||
* Create and Assign mode allows for both - creation of new tags and selection of existing tags.
|
||||
*/
|
||||
export enum TagsCreatorMode {
|
||||
CREATE,
|
||||
CREATE_AND_ASSIGN
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
<div class="adf-tags-creation">
|
||||
<p
|
||||
class="adf-no-tags-message"
|
||||
*ngIf="!tags.length && !tagNameControlVisible">
|
||||
{{ 'TAG.TAGS_CREATOR.NO_TAGS_CREATED' | translate }}
|
||||
</p>
|
||||
<div
|
||||
class="adf-tags-list"
|
||||
[class.adf-tags-list-with-scrollbar]="tagsListScrollbarVisible"
|
||||
#tagsList>
|
||||
<p
|
||||
*ngFor="let tag of tags"
|
||||
class="adf-tag adf-label-with-icon-button">
|
||||
{{ tag }}
|
||||
<button
|
||||
data-automation-id="remove-tag-button"
|
||||
mat-icon-button
|
||||
(click)="removeTag(tag)"
|
||||
[attr.title]="'TAG.TAGS_CREATOR.TOOLTIPS.DELETE_TAG' | translate"
|
||||
[hidden]="disabledTagsRemoving">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="adf-tag-name-field"
|
||||
*ngIf="(!tagNameControlVisible && tags.length) || tagNameControlVisible"
|
||||
[hidden]="!tagNameControlVisible">
|
||||
<mat-form-field *ngIf="tagNameControlVisible">
|
||||
<mat-icon matPrefix>search</mat-icon>
|
||||
<mat-label id="adf-tag-name-input-label">
|
||||
{{ 'TAG.TAGS_CREATOR.NAME' | translate }}
|
||||
</mat-label>
|
||||
<input
|
||||
#tagNameInput
|
||||
matInput
|
||||
autocomplete="off"
|
||||
[formControl]="tagNameControl"
|
||||
(keyup.enter)="addTag()"
|
||||
aria-labelledby="adf-tag-name-input-label"
|
||||
adf-auto-focus
|
||||
/>
|
||||
<mat-error [hidden]="!tagNameControl.invalid">{{ tagNameErrorMessageKey | translate }}</mat-error>
|
||||
</mat-form-field>
|
||||
<button
|
||||
data-automation-id="hide-tag-name-input-button"
|
||||
mat-icon-button
|
||||
(click)="hideNameInput()"
|
||||
[attr.title]="'TAG.TAGS_CREATOR.TOOLTIPS.HIDE_INPUT' | translate">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="adf-existing-tags-panel"
|
||||
*ngIf="existingTagsPanelVisible">
|
||||
<ng-container *ngIf="!spinnerVisible || existingTags">
|
||||
<span
|
||||
class="adf-create-tag-label"
|
||||
(click)="addTag()"
|
||||
[hidden]="tagNameControl.invalid || typing">
|
||||
{{ 'TAG.TAGS_CREATOR.CREATE_TAG' | translate : { tag: tagNameControl.value } }}
|
||||
</span>
|
||||
<p class="adf-existing-tags-label">
|
||||
{{ existingTagsLabelKey | translate }}
|
||||
</p>
|
||||
</ng-container>
|
||||
<div class="adf-tags-list">
|
||||
<mat-selection-list
|
||||
*ngIf="!spinnerVisible || existingTags"
|
||||
(selectionChange)="addExistingTagToTagsToAssign($event)"
|
||||
[disabled]="isOnlyCreateMode()">
|
||||
<mat-list-option
|
||||
*ngFor="let tagRow of existingTags"
|
||||
class="adf-tag"
|
||||
[value]="tagRow">
|
||||
{{ tagRow.entry.tag }}
|
||||
</mat-list-option>
|
||||
<p *ngIf="!existingTags?.length">{{ 'TAG.TAGS_CREATOR.NO_EXISTING_TAGS' | translate }}</p>
|
||||
</mat-selection-list>
|
||||
<mat-spinner
|
||||
*ngIf="spinnerVisible"
|
||||
[diameter]="50"
|
||||
[attr.aria-label]="'TAG.TAGS_CREATOR.TAGS_LOADING' | translate">
|
||||
</mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,144 +0,0 @@
|
||||
adf-tags-creator {
|
||||
display: block;
|
||||
margin-left: -24px;
|
||||
|
||||
&.adf-creator-with-existing-tags-panel {
|
||||
background-color: var(--theme-background-color);
|
||||
}
|
||||
|
||||
.adf-label-with-icon-button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adf-no-tags-message {
|
||||
margin-left: 9px;
|
||||
margin-top: 28.5px;
|
||||
margin-bottom: 0;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.adf-tag-name-field,
|
||||
.adf-tag-name-field[hidden] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 57px;
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-right: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-create-tag-label {
|
||||
color: var(--theme-primary-color);
|
||||
cursor: pointer;
|
||||
margin-top: -1px;
|
||||
padding-left: 27px;
|
||||
overflow-wrap: anywhere;
|
||||
display: inline-block;
|
||||
padding-right: 12px;
|
||||
overflow: auto;
|
||||
max-height: 7vh;
|
||||
}
|
||||
|
||||
.adf-tags-list {
|
||||
padding-left: 10px;
|
||||
padding-right: 0;
|
||||
overflow: auto;
|
||||
|
||||
&.adf-tags-list-with-scrollbar {
|
||||
padding-right: 7px;
|
||||
margin-right: -22px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-tag {
|
||||
margin-top: 0;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
& + .adf-tag {
|
||||
margin-top: -14px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-tags-creation {
|
||||
padding-left: 27px;
|
||||
padding-right: 22px;
|
||||
}
|
||||
|
||||
.adf-existing-tags-panel {
|
||||
background-color: var(--theme-card-background-color);
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
padding-top: 12px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
|
||||
.adf-existing-tags-label {
|
||||
font-size: 10px;
|
||||
color: var(--theme-secondary-text-color);
|
||||
padding-left: 12px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.adf-tags-list {
|
||||
padding-left: 18px;
|
||||
margin-top: -2px;
|
||||
padding-right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 75px;
|
||||
|
||||
.adf-tag {
|
||||
margin-bottom: 18px;
|
||||
margin-top: 0;
|
||||
padding-right: 12px;
|
||||
font-size: 14px;
|
||||
height: auto;
|
||||
width: unset;
|
||||
|
||||
& + .adf-tag {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.mat-list-item-content-reverse {
|
||||
padding: 0 6px;
|
||||
|
||||
.mat-pseudo-checkbox {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-spinner {
|
||||
margin: auto;
|
||||
overflow: unset;
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
.mat-list-item-disabled {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-dialog-actions {
|
||||
margin-right: -24px;
|
||||
background-color: inherit;
|
||||
|
||||
.adf-save-button {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
display: unset;
|
||||
}
|
||||
}
|
||||
@@ -1,689 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||
import { TagsCreatorComponent } from './tags-creator.component';
|
||||
import { NotificationService } from '@alfresco/adf-core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatError, MatFormField, MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { ContentDirectiveModule, TagsCreatorMode, TagService } from '@alfresco/adf-content-services';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { EMPTY, of, throwError } from 'rxjs';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MatListModule, MatSelectionList, MatSelectionListChange } from '@angular/material/list';
|
||||
|
||||
describe('TagsCreatorComponent', () => {
|
||||
let fixture: ComponentFixture<TagsCreatorComponent>;
|
||||
let component: TagsCreatorComponent;
|
||||
let tagService: TagService;
|
||||
let notificationService: NotificationService;
|
||||
|
||||
const tagNameFieldSelector = '.adf-tag-name-field';
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TagsCreatorComponent],
|
||||
imports: [
|
||||
ContentDirectiveModule,
|
||||
MatButtonModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatListModule,
|
||||
NoopAnimationsModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: TagService,
|
||||
useValue: {
|
||||
findTagByName: () => of(null),
|
||||
searchTags: () =>
|
||||
of({
|
||||
list: {
|
||||
entries: []
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
provide: NotificationService,
|
||||
useValue: {
|
||||
showError: () => ({})
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(TagsCreatorComponent);
|
||||
component = fixture.componentInstance;
|
||||
tagService = TestBed.inject(TagService);
|
||||
notificationService = TestBed.inject(NotificationService);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
function getNameInput(): HTMLInputElement {
|
||||
return fixture.debugElement.query(By.css(`.adf-tag-name-field input`))?.nativeElement;
|
||||
}
|
||||
|
||||
function getCreateTagLabel(): HTMLSpanElement {
|
||||
return fixture.debugElement.query(By.css('.adf-create-tag-label'))?.nativeElement;
|
||||
}
|
||||
|
||||
function getRemoveTagButtons(): HTMLButtonElement[] {
|
||||
const elements = fixture.debugElement.queryAll(By.css(`[data-automation-id="remove-tag-button"]`));
|
||||
return elements.map(el => el.nativeElement);
|
||||
}
|
||||
|
||||
function clickAtHideNameInputButton() {
|
||||
fixture.debugElement.query(By.css(`[data-automation-id="hide-tag-name-input-button"]`)).nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function getAddedTags(): string[] {
|
||||
const tagElements = fixture.debugElement.queryAll(By.css(`.adf-tags-creation .adf-tag`));
|
||||
return tagElements.map(el => el.nativeElement.firstChild.nodeValue.trim());
|
||||
}
|
||||
|
||||
function addTagToAddedList(tagName: string, addUsingEnter?: boolean, typingTimeout = 300): void {
|
||||
typeTag(tagName, typingTimeout);
|
||||
|
||||
if (addUsingEnter) {
|
||||
getNameInput().dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
|
||||
} else {
|
||||
getCreateTagLabel().click();
|
||||
}
|
||||
|
||||
tick(300);
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function typeTag(tagName: string, timeout = 300): void {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const tagNameInput = getNameInput();
|
||||
tagNameInput.value = tagName;
|
||||
tagNameInput.dispatchEvent(new InputEvent('input'));
|
||||
|
||||
tick(timeout);
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function findSelectionList(): MatSelectionList {
|
||||
return fixture.debugElement.query(By.directive(MatSelectionList)).componentInstance;
|
||||
}
|
||||
|
||||
describe('Created tags list', () => {
|
||||
it('should display no tags created message after initialization', () => {
|
||||
const message = fixture.debugElement.query(By.css('.adf-no-tags-message')).nativeElement.textContent.trim();
|
||||
expect(message).toBe('TAG.TAGS_CREATOR.NO_TAGS_CREATED');
|
||||
});
|
||||
|
||||
it('should display all tags which have been typed in input and accepted using enter', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
const tag2 = 'Tag 2';
|
||||
|
||||
addTagToAddedList(tag1, true);
|
||||
addTagToAddedList(tag2, true);
|
||||
|
||||
const tagElements = getAddedTags();
|
||||
expect(tagElements.length).toBe(2);
|
||||
expect(tagElements[0]).toBe(tag1);
|
||||
expect(tagElements[1]).toBe(tag2);
|
||||
}));
|
||||
|
||||
it('should display all tags which have been typed in input and accepted by clicking at create label', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
const tag2 = 'Tag 2';
|
||||
|
||||
addTagToAddedList(tag1);
|
||||
addTagToAddedList(tag2);
|
||||
|
||||
const tagElements = getAddedTags();
|
||||
expect(tagElements).toEqual([tag1, tag2]);
|
||||
}));
|
||||
|
||||
it('should not add tag if contains only spaces', fakeAsync(() => {
|
||||
addTagToAddedList(' ', true);
|
||||
|
||||
expect(getAddedTags().length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should not add tag if field is empty', fakeAsync(() => {
|
||||
addTagToAddedList('', true);
|
||||
|
||||
expect(getAddedTags().length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should not duplicate already added tag', fakeAsync(() => {
|
||||
const tag = 'Some tag';
|
||||
|
||||
addTagToAddedList(tag, true);
|
||||
addTagToAddedList(tag, true);
|
||||
|
||||
expect(getAddedTags().length).toBe(1);
|
||||
}));
|
||||
|
||||
it('should not duplicate already existing tag', fakeAsync(() => {
|
||||
const tag = 'Tag';
|
||||
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(of({
|
||||
entry: {
|
||||
tag,
|
||||
id: 'tag-1'
|
||||
}
|
||||
}));
|
||||
addTagToAddedList(tag, true);
|
||||
|
||||
expect(getAddedTags().length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should not add tag if hit enter during tags loading', fakeAsync(() => {
|
||||
addTagToAddedList('Tag', true, 0);
|
||||
expect(getAddedTags().length).toBe(0);
|
||||
}));
|
||||
|
||||
|
||||
it('should remove specific tag after clicking at remove icon', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
const tag2 = 'Tag 2';
|
||||
|
||||
addTagToAddedList(tag1);
|
||||
addTagToAddedList(tag2);
|
||||
|
||||
getRemoveTagButtons()[0].click();
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const tagElements = getAddedTags();
|
||||
expect(tagElements).toEqual([tag2]);
|
||||
}));
|
||||
|
||||
it('should hide button for removing tag if disabledTagsRemoving is true', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
component.disabledTagsRemoving = true;
|
||||
|
||||
addTagToAddedList(tag1);
|
||||
tick();
|
||||
|
||||
expect(getRemoveTagButtons()[0].hasAttribute('hidden')).toBeTrue();
|
||||
}));
|
||||
|
||||
it('should show button for removing tag if disabledTagsRemoving is false', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
component.disabledTagsRemoving = false;
|
||||
|
||||
addTagToAddedList(tag1);
|
||||
tick();
|
||||
|
||||
expect(getRemoveTagButtons()[0].hasAttribute('hidden')).toBeFalse();
|
||||
}));
|
||||
|
||||
it('should display tags passed by tags input', () => {
|
||||
component.tags = ['Passed tag 1', 'Passed tag 2'];
|
||||
fixture.detectChanges();
|
||||
expect(getAddedTags()).toEqual(component.tags);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tag name field', () => {
|
||||
it('should be visible if tagNameControlVisible is true', () => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const tagNameField = fixture.debugElement.query(By.css(tagNameFieldSelector));
|
||||
expect(tagNameField).toBeTruthy();
|
||||
expect(tagNameField.nativeElement.hasAttribute('hidden')).toBeFalsy();
|
||||
expect(tagNameField.query(By.directive(MatFormField))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be hidden after clicking button for hiding input', fakeAsync(() => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
|
||||
clickAtHideNameInputButton();
|
||||
|
||||
const tagNameField = fixture.debugElement.query(By.css(tagNameFieldSelector));
|
||||
expect(tagNameField).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should input be autofocused', fakeAsync(() => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
expect(getNameInput()).toBe(document.activeElement as HTMLInputElement);
|
||||
}));
|
||||
|
||||
it('should input be autofocused after showing input second time', fakeAsync(() => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
|
||||
clickAtHideNameInputButton();
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
|
||||
expect(getNameInput()).toBe(document.activeElement as HTMLInputElement);
|
||||
}));
|
||||
|
||||
describe('Errors', () => {
|
||||
function getFirstError(): string {
|
||||
const error = fixture.debugElement.query(By.directive(MatError));
|
||||
return error.nativeElement.textContent;
|
||||
}
|
||||
|
||||
it('should show error for only spaces', fakeAsync(() => {
|
||||
typeTag(' ');
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.EMPTY_TAG');
|
||||
}));
|
||||
|
||||
it('should show error for required', fakeAsync(() => {
|
||||
typeTag('');
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.REQUIRED');
|
||||
}));
|
||||
|
||||
it('should show error when duplicated already added tag', fakeAsync(() => {
|
||||
const tag = 'Some tag';
|
||||
|
||||
addTagToAddedList(tag);
|
||||
typeTag(tag);
|
||||
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.ALREADY_ADDED_TAG');
|
||||
}));
|
||||
|
||||
it('should show error when duplicated already existing tag', fakeAsync(() => {
|
||||
const tag = 'Some tag';
|
||||
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(of({
|
||||
entry: {
|
||||
tag,
|
||||
id: 'tag-1'
|
||||
}
|
||||
}));
|
||||
typeTag(tag);
|
||||
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.EXISTING_TAG');
|
||||
}));
|
||||
|
||||
it('should error for required when not typed anything and blur input', fakeAsync(() => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
|
||||
getNameInput().blur();
|
||||
fixture.detectChanges();
|
||||
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.REQUIRED');
|
||||
|
||||
flush();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Existing tags panel', () => {
|
||||
function getPanel(): DebugElement {
|
||||
return fixture.debugElement.query(By.css(`.adf-existing-tags-panel`));
|
||||
}
|
||||
|
||||
it('should be visible when input is visible and something is typed in input', fakeAsync(() => {
|
||||
typeTag('some tag');
|
||||
|
||||
expect(getPanel()).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should not be visible initially when input is hidden and nothing was typed', () => {
|
||||
expect(getPanel()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not be visible when input is visible and empty string is typed in input', fakeAsync(() => {
|
||||
typeTag(' ');
|
||||
|
||||
expect(getPanel()).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should not be visible when input is visible and nothing has been typed', () => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getPanel()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not be visible when something has been typed and input has been hidden', fakeAsync(() => {
|
||||
typeTag('some tag');
|
||||
|
||||
clickAtHideNameInputButton();
|
||||
|
||||
expect(getPanel()).toBeFalsy();
|
||||
}));
|
||||
|
||||
describe('Label for tag creation', () => {
|
||||
it('should be visible', fakeAsync(() => {
|
||||
typeTag('some tag');
|
||||
|
||||
expect(getCreateTagLabel()).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should not be visible if typed only spaces', fakeAsync(() => {
|
||||
typeTag(' ');
|
||||
|
||||
expect(getCreateTagLabel()).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should not be visible if required error occurs', fakeAsync(() => {
|
||||
typeTag('');
|
||||
expect(getCreateTagLabel()).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should not be visible when trying to duplicate already added tag', fakeAsync(() => {
|
||||
const tag = 'Some tag';
|
||||
|
||||
addTagToAddedList(tag);
|
||||
typeTag(tag);
|
||||
|
||||
expect(getCreateTagLabel().hasAttribute('hidden')).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should not be visible when trying to duplicate already existing tag', fakeAsync(() => {
|
||||
const tag = 'Tag';
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(of({
|
||||
entry: {
|
||||
tag,
|
||||
id: 'tag-1'
|
||||
}
|
||||
}));
|
||||
typeTag(tag);
|
||||
expect(getCreateTagLabel().hasAttribute('hidden')).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should not be visible if typed nothing', () => {
|
||||
component.tagNameControlVisible = true;
|
||||
fixture.detectChanges();
|
||||
expect(getCreateTagLabel()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not be visible during typing', fakeAsync(() => {
|
||||
typeTag('some tag', 0);
|
||||
expect(getCreateTagLabel()).toBeFalsy();
|
||||
discardPeriodicTasks();
|
||||
flush();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Existing tags', () => {
|
||||
function getExistingTags(): string[] {
|
||||
const tagElements = fixture.debugElement.queryAll(By.css(`.adf-existing-tags-panel .adf-tag .mat-list-text`));
|
||||
return tagElements.map(el => el.nativeElement.firstChild.nodeValue.trim());
|
||||
}
|
||||
|
||||
it('should call findTagByName on tagService using name set in input', fakeAsync(() => {
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(EMPTY);
|
||||
|
||||
const name = 'Tag';
|
||||
typeTag(name);
|
||||
|
||||
expect(tagService.findTagByName).toHaveBeenCalledWith(name);
|
||||
}));
|
||||
|
||||
it('should call searchTags on tagService using name set in input and correct params', fakeAsync(() => {
|
||||
spyOn(tagService, 'searchTags').and.returnValue(EMPTY);
|
||||
|
||||
const name = 'Tag';
|
||||
typeTag(name);
|
||||
|
||||
expect(tagService.searchTags).toHaveBeenCalledWith(name, { orderBy: 'tag', direction: 'asc' },
|
||||
false, 0, 15 );
|
||||
}));
|
||||
|
||||
it('should display loaded existing tags', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
const tag2 = 'Tag 2';
|
||||
|
||||
spyOn(tagService, 'searchTags').and.returnValue(
|
||||
of({
|
||||
list: {
|
||||
entries: [
|
||||
{ entry: { tag: tag1 } as any },
|
||||
{ entry: { tag: tag2 } as any }
|
||||
],
|
||||
pagination: {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
typeTag('Tag');
|
||||
|
||||
const tagElements = getExistingTags();
|
||||
expect(tagElements).toEqual([tag1, tag2]);
|
||||
}));
|
||||
|
||||
it('should exclude tags passed through tags input from loaded existing tags', fakeAsync(() => {
|
||||
const tag1 = 'Tag 1';
|
||||
const tag2 = 'Tag 2';
|
||||
component.tags = [tag1];
|
||||
|
||||
spyOn(tagService, 'searchTags').and.returnValue(
|
||||
of({
|
||||
list: {
|
||||
entries: [
|
||||
{ entry: { tag: tag1 } as any },
|
||||
{ entry: { tag: tag2 } as any }
|
||||
],
|
||||
pagination: {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
typeTag('Tag');
|
||||
|
||||
expect(getExistingTags()).toEqual([tag2]);
|
||||
}));
|
||||
|
||||
it('should not display existing tags if searching fails', fakeAsync(() => {
|
||||
spyOn(notificationService, 'showError');
|
||||
spyOn(tagService, 'searchTags').and.returnValue(throwError({}));
|
||||
|
||||
typeTag('Tag');
|
||||
|
||||
expect(getExistingTags()).toEqual([]);
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('TAG.TAGS_CREATOR.ERRORS.FETCH_TAGS');
|
||||
}));
|
||||
|
||||
it('should display exact tag', fakeAsync(() => {
|
||||
const tag = 'Tag';
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(of({
|
||||
entry: {
|
||||
tag,
|
||||
id: 'tag-1'
|
||||
}
|
||||
}));
|
||||
|
||||
typeTag(tag);
|
||||
|
||||
const tagElements = getExistingTags();
|
||||
expect(tagElements).toEqual([tag]);
|
||||
}));
|
||||
|
||||
it('should not display exact tag if that tag was passed through tags input', fakeAsync(() => {
|
||||
const tag = 'Tag';
|
||||
component.tags = [tag];
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(of({
|
||||
entry: {
|
||||
tag,
|
||||
id: 'tag-1'
|
||||
}
|
||||
}));
|
||||
|
||||
typeTag(tag);
|
||||
|
||||
expect(getExistingTags()).toEqual([]);
|
||||
}));
|
||||
|
||||
it('should not display exact tag if exact tag loading fails', fakeAsync(() => {
|
||||
spyOn(notificationService, 'showError');
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(throwError({}));
|
||||
|
||||
typeTag('Tag');
|
||||
|
||||
expect(getExistingTags()).toEqual([]);
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('TAG.TAGS_CREATOR.ERRORS.FETCH_TAGS');
|
||||
}));
|
||||
|
||||
it('should exact tag be above others existing tags when there are some different existing tags than exact tag', fakeAsync(() => {
|
||||
const tag = 'Tag';
|
||||
const tag1 = 'Tag 1';
|
||||
const tag2 = 'Tag 2';
|
||||
|
||||
spyOn(tagService, 'findTagByName').and.returnValue(of({
|
||||
entry: {
|
||||
tag,
|
||||
id: 'tag-1'
|
||||
}
|
||||
}));
|
||||
spyOn(tagService, 'searchTags').and.returnValue(
|
||||
of({
|
||||
list: {
|
||||
entries: [
|
||||
{ entry: { tag: tag1 } as any },
|
||||
{ entry: { tag: tag2 } as any }
|
||||
],
|
||||
pagination: {}
|
||||
}
|
||||
})
|
||||
);
|
||||
typeTag(tag);
|
||||
|
||||
const tagElements = getExistingTags();
|
||||
expect(tagElements).toEqual([tag, tag1, tag2]);
|
||||
}));
|
||||
|
||||
it('should selection be disabled if mode is Create', fakeAsync(() => {
|
||||
component.mode = TagsCreatorMode.CREATE;
|
||||
spyOn(tagService, 'searchTags').and.returnValue(
|
||||
of({
|
||||
list: {
|
||||
entries: [
|
||||
{ entry: { tag: 'tag' } as any }
|
||||
],
|
||||
pagination: {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
typeTag('Tag');
|
||||
|
||||
expect(findSelectionList().disabled).toBeTrue();
|
||||
}));
|
||||
|
||||
it('should selection be enabled if mode is Create And Assign', fakeAsync(() => {
|
||||
component.mode = TagsCreatorMode.CREATE_AND_ASSIGN;
|
||||
spyOn(tagService, 'searchTags').and.returnValue(
|
||||
of({
|
||||
list: {
|
||||
entries: [
|
||||
{ entry: { tag: 'tag' } as any }
|
||||
],
|
||||
pagination: {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
typeTag('Tag');
|
||||
|
||||
expect(findSelectionList().disabled).toBeFalse();
|
||||
}));
|
||||
|
||||
it('should select existing tag when selectionChange event emits', fakeAsync(() => {
|
||||
const selectedTag = { entry: { tag: 'tag1' } as any };
|
||||
const leftTag = 'tag2';
|
||||
component.mode = TagsCreatorMode.CREATE_AND_ASSIGN;
|
||||
spyOn(tagService, 'searchTags').and.returnValue(
|
||||
of({
|
||||
list: {
|
||||
entries: [
|
||||
selectedTag,
|
||||
{ entry: { tag: leftTag } as any }
|
||||
],
|
||||
pagination: {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
typeTag('Tag');
|
||||
findSelectionList().selectionChange.emit({
|
||||
options: [{
|
||||
value: selectedTag
|
||||
}]
|
||||
} as MatSelectionListChange);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getAddedTags()).toEqual([selectedTag.entry.tag]);
|
||||
expect(getExistingTags()).toEqual([leftTag]);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Spinner', () => {
|
||||
function getSpinner(): DebugElement {
|
||||
return fixture.debugElement.query(By.css(`.mat-progress-spinner`));
|
||||
}
|
||||
|
||||
it('should be displayed when existing tags are loading', fakeAsync(() => {
|
||||
typeTag('tag', 0);
|
||||
|
||||
const spinner = getSpinner();
|
||||
expect(spinner).toBeTruthy();
|
||||
|
||||
discardPeriodicTasks();
|
||||
flush();
|
||||
}));
|
||||
|
||||
it('should not be displayed when existing tags stopped loading', fakeAsync(() => {
|
||||
typeTag('tag');
|
||||
|
||||
const spinner = getSpinner();
|
||||
expect(spinner).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should have correct diameter', fakeAsync(() => {
|
||||
typeTag('tag', 0);
|
||||
|
||||
const spinner = getSpinner();
|
||||
expect(spinner.componentInstance.diameter).toBe(50);
|
||||
|
||||
discardPeriodicTasks();
|
||||
flush();
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,427 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TagEntry, TagPaging } from '@alfresco/js-api';
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { FormControl, Validators } from '@angular/forms';
|
||||
import { debounce, distinctUntilChanged, finalize, first, map, takeUntil, tap } from 'rxjs/operators';
|
||||
import { EMPTY, forkJoin, Observable, Subject, timer } from 'rxjs';
|
||||
import { NotificationService } from '@alfresco/adf-core';
|
||||
import { TagsCreatorMode } from './tags-creator-mode';
|
||||
import { MatSelectionListChange } from '@angular/material/list';
|
||||
import { TagService } from '../services/tag.service';
|
||||
|
||||
interface TagNameControlErrors {
|
||||
duplicatedExistingTag?: boolean;
|
||||
duplicatedAddedTag?: boolean;
|
||||
emptyTag?: boolean;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_TAGS_SORTING = {
|
||||
orderBy: 'tag',
|
||||
direction: 'asc'
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows to create multiple tags. That component contains input and two lists. Top list is all created tags, bottom list is searched tags based on input's value.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'adf-tags-creator',
|
||||
templateUrl: './tags-creator.component.html',
|
||||
styleUrls: ['./tags-creator.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* Mode for component.
|
||||
* In Create mode we can't select existing tags, we can only create them.
|
||||
* In Create and Assign mode we can both - create tags and select existing tags.
|
||||
*/
|
||||
@Input()
|
||||
mode: TagsCreatorMode;
|
||||
|
||||
/**
|
||||
* False if tags can be removed from top list, true otherwise.
|
||||
*/
|
||||
@Input()
|
||||
disabledTagsRemoving = false;
|
||||
|
||||
/**
|
||||
* Default top list.
|
||||
* @param tags tags which should be displayed as default tags for top list.
|
||||
*/
|
||||
@Input()
|
||||
set tags(tags: string[]) {
|
||||
this._tags = [...tags];
|
||||
this._spinnerVisible = true;
|
||||
this._initialExistingTags = null;
|
||||
this._existingTags = null;
|
||||
this.loadTags(this.tagNameControl.value);
|
||||
this.tagNameControl.updateValueAndValidity();
|
||||
}
|
||||
|
||||
get tags(): string[] {
|
||||
return this._tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if input for tags creation/searching should be visible. When input is hidden then panel of existing tags is hidden as well.
|
||||
* @param tagNameControlVisible true if input should be visible, false otherwise.
|
||||
*/
|
||||
@Input()
|
||||
set tagNameControlVisible(tagNameControlVisible: boolean) {
|
||||
this._tagNameControlVisible = tagNameControlVisible;
|
||||
if (tagNameControlVisible) {
|
||||
this._existingTagsPanelVisible = !!this.tagNameControl.value.trim();
|
||||
setTimeout(() => {
|
||||
this.tagNameInputElement.nativeElement.scrollIntoView();
|
||||
});
|
||||
} else {
|
||||
this._existingTagsPanelVisible = false;
|
||||
}
|
||||
this.existingTagsPanelVisibilityChange.emit(this.existingTagsPanelVisible);
|
||||
}
|
||||
|
||||
get tagNameControlVisible(): boolean {
|
||||
return this._tagNameControlVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted when bottom list is showing or hiding.
|
||||
*/
|
||||
@Output()
|
||||
existingTagsPanelVisibilityChange = new EventEmitter<boolean>();
|
||||
@Output()
|
||||
/**
|
||||
* Emitted when tags in top list are changed.
|
||||
*/
|
||||
tagsChange = new EventEmitter<string[]>();
|
||||
/**
|
||||
* Emitted when input is showing or hiding.
|
||||
*/
|
||||
@Output()
|
||||
tagNameControlVisibleChange = new EventEmitter<boolean>();
|
||||
|
||||
readonly nameErrorMessagesByErrors = new Map<keyof TagNameControlErrors, string>([
|
||||
['duplicatedExistingTag', 'EXISTING_TAG'],
|
||||
['duplicatedAddedTag', 'ALREADY_ADDED_TAG'],
|
||||
['emptyTag', 'EMPTY_TAG'],
|
||||
['required', 'REQUIRED']
|
||||
]);
|
||||
|
||||
private readonly existingTagsListLimit = 15;
|
||||
|
||||
private exactTagSet$ = new Subject<void>();
|
||||
private _tags: string[] = [];
|
||||
private _tagNameControl = new FormControl<string>(
|
||||
'',
|
||||
[
|
||||
this.validateIfNotAlreadyAdded.bind(this),
|
||||
Validators.required,
|
||||
this.validateEmptyTag
|
||||
],
|
||||
this.validateIfNotExistingTag.bind(this)
|
||||
);
|
||||
private _tagNameControlVisible = false;
|
||||
private _existingTags: TagEntry[];
|
||||
private _initialExistingTags: TagEntry[];
|
||||
private onDestroy$ = new Subject<void>();
|
||||
private _tagNameErrorMessageKey = '';
|
||||
private _spinnerVisible = false;
|
||||
private _typing = false;
|
||||
private _tagsListScrollbarVisible = false;
|
||||
private cancelExistingTagsLoading$ = new Subject<void>();
|
||||
private existingExactTag: TagEntry;
|
||||
private _existingTagsPanelVisible: boolean;
|
||||
private _existingTagsLabelKey: string;
|
||||
|
||||
@ViewChild('tagsList')
|
||||
private tagsListElement: ElementRef;
|
||||
@ViewChild('tagNameInput')
|
||||
private tagNameInputElement: ElementRef;
|
||||
|
||||
constructor(
|
||||
private tagService: TagService,
|
||||
private notificationService: NotificationService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.tagNameControl.valueChanges
|
||||
.pipe(
|
||||
map((name: string) => name.trim()),
|
||||
distinctUntilChanged(),
|
||||
tap((name: string) => {
|
||||
this._typing = true;
|
||||
if (name) {
|
||||
this._spinnerVisible = true;
|
||||
this._existingTagsPanelVisible = true;
|
||||
} else {
|
||||
this._existingTagsPanelVisible = false;
|
||||
}
|
||||
this.existingTagsPanelVisibilityChange.emit(this.existingTagsPanelVisible);
|
||||
this.cancelExistingTagsLoading$.next();
|
||||
this._initialExistingTags = null;
|
||||
this._existingTags = null;
|
||||
}),
|
||||
debounce((name: string) => (name ? timer(300) : EMPTY)),
|
||||
takeUntil(this.onDestroy$)
|
||||
)
|
||||
.subscribe((name: string) => this.onTagNameControlValueChange(name));
|
||||
|
||||
this.tagNameControl.statusChanges
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(() => this.setTagNameControlErrorMessageKey());
|
||||
|
||||
this.setTagNameControlErrorMessageKey();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
this.cancelExistingTagsLoading$.next();
|
||||
this.cancelExistingTagsLoading$.complete();
|
||||
}
|
||||
|
||||
@HostBinding('class.adf-creator-with-existing-tags-panel')
|
||||
get hostClass(): boolean {
|
||||
return this.existingTagsPanelVisible;
|
||||
}
|
||||
|
||||
get tagNameControl(): FormControl<string> {
|
||||
return this._tagNameControl;
|
||||
}
|
||||
|
||||
get existingTags(): TagEntry[] {
|
||||
return this._existingTags;
|
||||
}
|
||||
|
||||
get tagNameErrorMessageKey(): string {
|
||||
return this._tagNameErrorMessageKey;
|
||||
}
|
||||
|
||||
get spinnerVisible(): boolean {
|
||||
return this._spinnerVisible;
|
||||
}
|
||||
|
||||
get typing(): boolean {
|
||||
return this._typing;
|
||||
}
|
||||
|
||||
get tagsListScrollbarVisible(): boolean {
|
||||
return this._tagsListScrollbarVisible;
|
||||
}
|
||||
|
||||
get existingTagsPanelVisible(): boolean {
|
||||
return this._existingTagsPanelVisible;
|
||||
}
|
||||
|
||||
get existingTagsLabelKey(): string {
|
||||
return this._existingTagsLabelKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide input for typing name for new tag or for searching. When input is hidden then panel of existing tags is hidden as well.
|
||||
*/
|
||||
hideNameInput(): void {
|
||||
this.tagNameControlVisible = false;
|
||||
this._existingTagsPanelVisible = false;
|
||||
this.existingTagsPanelVisibilityChange.emit(this.existingTagsPanelVisible);
|
||||
this.tagNameControlVisibleChange.emit(this.tagNameControlVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tags to top list using value which is set in input. Adding tag is not allowed when value in input is invalid
|
||||
* or if user is still typing what means that validation for input is not called yet.
|
||||
*/
|
||||
addTag(): void {
|
||||
if (!this._typing && !this.tagNameControl.invalid) {
|
||||
this.tags.push(this.tagNameControl.value.trim());
|
||||
this.hideNameInput();
|
||||
this.tagNameControl.setValue('');
|
||||
this.checkScrollbarVisibility();
|
||||
this.tagNameControl.markAsUntouched();
|
||||
this.tagsChange.emit(this.tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove tag from top list. In case that tag was part of search result then that tag is moved to bottom list
|
||||
* (list of existing tags) after removing so user can reselect it again later.
|
||||
* @param tag tag's name which should be removed from top list.
|
||||
*/
|
||||
removeTag(tag: string): void {
|
||||
this.removeTagFromArray(this.tags, tag);
|
||||
this.tagNameControl.updateValueAndValidity();
|
||||
this.updateExistingTagsListOnRemoveFromTagsToConfirm(tag);
|
||||
this.checkScrollbarVisibility();
|
||||
this.tagsChange.emit(this.tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when user selects any tag from list of existing tags. It moves tag from existing tags list to top list.
|
||||
* @param change
|
||||
*/
|
||||
addExistingTagToTagsToAssign(change: MatSelectionListChange): void {
|
||||
const selectedTag: TagEntry = change.options[0].value;
|
||||
this.tags.push(selectedTag.entry.tag);
|
||||
this.removeTagFromArray(this.existingTags, selectedTag);
|
||||
this.tagNameControl.updateValueAndValidity();
|
||||
this.exactTagSet$.next();
|
||||
this.tagsChange.emit(this.tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if component is in Create mode.
|
||||
* @return true if Create mode, false otherwise.
|
||||
*/
|
||||
isOnlyCreateMode(): boolean {
|
||||
return this.mode === TagsCreatorMode.CREATE;
|
||||
}
|
||||
|
||||
private onTagNameControlValueChange(name: string): void {
|
||||
this.tagNameControl.markAsTouched();
|
||||
this.loadTags(name);
|
||||
}
|
||||
|
||||
private loadTags(name: string) {
|
||||
if (name) {
|
||||
forkJoin({
|
||||
exactResult: this.tagService.findTagByName(name),
|
||||
searchedResult: this.tagService.searchTags(name, DEFAULT_TAGS_SORTING, false, 0, this.existingTagsListLimit)
|
||||
}).pipe(
|
||||
takeUntil(this.cancelExistingTagsLoading$),
|
||||
finalize(() => (this._typing = false))
|
||||
).subscribe(({ exactResult, searchedResult }: {
|
||||
exactResult: TagEntry;
|
||||
searchedResult: TagPaging;
|
||||
}) => {
|
||||
if (exactResult) {
|
||||
this.existingExactTag = exactResult;
|
||||
this.removeExactTagFromSearchedResult(searchedResult);
|
||||
searchedResult.list.entries.unshift(exactResult);
|
||||
} else {
|
||||
this.existingExactTag = null;
|
||||
}
|
||||
|
||||
this._initialExistingTags = searchedResult.list.entries;
|
||||
this.excludeAlreadyAddedTags(this._initialExistingTags);
|
||||
this.exactTagSet$.next();
|
||||
this._spinnerVisible = false;
|
||||
}, () => {
|
||||
this.notificationService.showError('TAG.TAGS_CREATOR.ERRORS.FETCH_TAGS');
|
||||
this._spinnerVisible = false;
|
||||
});
|
||||
} else {
|
||||
this.existingExactTag = null;
|
||||
}
|
||||
}
|
||||
|
||||
private removeExactTagFromSearchedResult(searchedResult: TagPaging): void {
|
||||
const exactTagIndex = searchedResult.list.entries.findIndex(
|
||||
(row) => this.compareTags(row.entry.tag, this.existingExactTag.entry.tag)
|
||||
);
|
||||
|
||||
if (exactTagIndex > -1) {
|
||||
searchedResult.list.entries.splice(exactTagIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private validateIfNotExistingTag(tagNameControl: FormControl<string>): Observable<TagNameControlErrors | null> {
|
||||
return this.exactTagSet$.pipe(
|
||||
map<void, TagNameControlErrors | null>(() =>
|
||||
this.compareTags(tagNameControl.value, this.existingExactTag?.entry?.tag) ? { duplicatedExistingTag: true }
|
||||
: null
|
||||
), first()
|
||||
);
|
||||
}
|
||||
|
||||
private validateIfNotAlreadyAdded(tagNameControl: FormControl<string>): TagNameControlErrors | null {
|
||||
return this.tags.some((tag) => this.compareTags(tag, tagNameControl.value))
|
||||
? { duplicatedAddedTag: true }
|
||||
: null;
|
||||
}
|
||||
|
||||
private compareTags(tagName1?: string, tagName2?: string): boolean {
|
||||
return tagName1?.trim().toUpperCase() === tagName2?.trim().toUpperCase();
|
||||
}
|
||||
|
||||
private validateEmptyTag(tagNameControl: FormControl<string>): TagNameControlErrors | null {
|
||||
return tagNameControl.value.length && !tagNameControl.value.trim()
|
||||
? { emptyTag: true }
|
||||
: null;
|
||||
}
|
||||
|
||||
private setTagNameControlErrorMessageKey(): void {
|
||||
this._tagNameErrorMessageKey = this.tagNameControl.invalid
|
||||
? `TAG.TAGS_CREATOR.ERRORS.${this.nameErrorMessagesByErrors.get(
|
||||
Object.keys(this.tagNameControl.errors)[0] as keyof TagNameControlErrors
|
||||
)}`
|
||||
: '';
|
||||
}
|
||||
|
||||
private checkScrollbarVisibility(): void {
|
||||
setTimeout(() => {
|
||||
this._tagsListScrollbarVisible =
|
||||
this.tagsListElement.nativeElement.scrollHeight >
|
||||
this.tagsListElement.nativeElement.clientHeight;
|
||||
});
|
||||
}
|
||||
|
||||
private removeTagFromArray<T>(tags: T[], tag: T) {
|
||||
tags.splice(tags.indexOf(tag), 1);
|
||||
}
|
||||
|
||||
private updateExistingTagsListOnRemoveFromTagsToConfirm(tag: string) {
|
||||
const entryForTagAddedToExistingTags = this._initialExistingTags?.find(
|
||||
(tagEntry) => tagEntry.entry.tag === tag
|
||||
);
|
||||
if (entryForTagAddedToExistingTags) {
|
||||
this.existingTags.unshift(entryForTagAddedToExistingTags);
|
||||
if (this.existingExactTag) {
|
||||
if (tag !== this.existingExactTag.entry.tag) {
|
||||
this.removeTagFromArray(this.existingTags, this.existingExactTag);
|
||||
this.sortExistingTags();
|
||||
this.existingTags.unshift(this.existingExactTag);
|
||||
}
|
||||
} else {
|
||||
this.sortExistingTags();
|
||||
}
|
||||
this.exactTagSet$.next();
|
||||
}
|
||||
}
|
||||
|
||||
private sortExistingTags() {
|
||||
this.existingTags.sort((tagEntry1, tagEntry2) =>
|
||||
tagEntry1.entry.tag.localeCompare(tagEntry2.entry.tag));
|
||||
}
|
||||
|
||||
private excludeAlreadyAddedTags(tags: TagEntry[]) {
|
||||
this._existingTags = tags.filter((tag) => !this.tags.includes(tag.entry.tag));
|
||||
}
|
||||
}
|
||||
@@ -91,13 +91,7 @@ export class FormModel implements ProcessFormModel {
|
||||
processVariables: ProcessVariableModel[] = [];
|
||||
variables: FormVariableModel[] = [];
|
||||
|
||||
constructor(
|
||||
json?: any,
|
||||
formValues?: FormValues,
|
||||
readOnly: boolean = false,
|
||||
protected formService?: FormValidationService,
|
||||
enableFixedSpace?: boolean
|
||||
) {
|
||||
constructor(json?: any, formValues?: FormValues, readOnly: boolean = false, protected formService?: FormValidationService, enableFixedSpace?: boolean) {
|
||||
this.readOnly = readOnly;
|
||||
this.json = json;
|
||||
|
||||
@@ -421,10 +415,8 @@ export class FormModel implements ProcessFormModel {
|
||||
}
|
||||
|
||||
setNodeIdValueForViewersLinkedToUploadWidget(linkedUploadWidgetContentSelected: UploadWidgetContentLinkModel) {
|
||||
const linkedWidgetType = linkedUploadWidgetContentSelected?.options?.linkedWidgetType ?? 'uploadWidget';
|
||||
|
||||
const subscribedViewers = this.fieldsCache.filter(field =>
|
||||
linkedUploadWidgetContentSelected.uploadWidgetId === field.params[linkedWidgetType]
|
||||
linkedUploadWidgetContentSelected.uploadWidgetId === field.params['uploadWidget']
|
||||
);
|
||||
|
||||
subscribedViewers.forEach(viewer => {
|
||||
|
||||
@@ -16,13 +16,11 @@
|
||||
*/
|
||||
|
||||
import { ContentLinkModel } from './content-link.model';
|
||||
export class UploadWidgetContentLinkModelOptions {
|
||||
linkedWidgetType: string;
|
||||
}
|
||||
|
||||
export class UploadWidgetContentLinkModel extends ContentLinkModel {
|
||||
uploadWidgetId: string;
|
||||
|
||||
constructor(obj?: any, uploadWidgetId?: string, public options?: UploadWidgetContentLinkModelOptions) {
|
||||
constructor(obj?: any, uploadWidgetId?: string) {
|
||||
super(obj);
|
||||
this.uploadWidgetId = uploadWidgetId;
|
||||
}
|
||||
|
||||
@@ -116,14 +116,10 @@
|
||||
.adf-login-button {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
line-height: normal;
|
||||
line-height: 38px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.adf-login-button .mat-button-wrapper {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.adf-login-button-label {
|
||||
color: var(--theme-accent-color-default-contrast);
|
||||
}
|
||||
@@ -148,7 +144,6 @@
|
||||
.adf-interactive-login-label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
line-height: 38px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,9 +96,6 @@
|
||||
--adf-theme-mat-grey-color-a200: mat.get-color-from-palette(mat.$grey-palette, A200),
|
||||
--adf-theme-mat-grey-color-a400: mat.get-color-from-palette(mat.$grey-palette, A400),
|
||||
--adf-theme-mat-grey-color-50: mat.get-color-from-palette(mat.$grey-palette, 50),
|
||||
|
||||
// spacing
|
||||
--adf-theme-spacing: map-get($custom-css-variables, 'theme-adf-spacing')
|
||||
);
|
||||
|
||||
// propagates SCSS variables into the CSS variables scope
|
||||
@@ -117,6 +114,5 @@ $adf-custom-theme-sizes: (
|
||||
'theme-adf-icon-1-font-size': 17px,
|
||||
'theme-adf-picture-1-font-size': 18px,
|
||||
'theme-adf-task-footer-font-size': 18px,
|
||||
'theme-adf-task-title-font-size': 18px,
|
||||
'theme-adf-spacing': 16px
|
||||
'theme-adf-task-title-font-size': 18px
|
||||
);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fakeEnvironmentList } from '../../common/mock/environment.mock';
|
||||
import { ApplicationInstanceModel } from '../models/application-instance.model';
|
||||
|
||||
export const fakeApplicationInstance: ApplicationInstanceModel[] = [
|
||||
@@ -23,9 +22,3 @@ export const fakeApplicationInstance: ApplicationInstanceModel[] = [
|
||||
{ name: 'application-new-2', createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending', theme: 'theme-2', icon: 'favorite_border' },
|
||||
{ name: 'application-new-3', createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending' }
|
||||
];
|
||||
|
||||
export const fakeApplicationInstanceWithEnvironment: ApplicationInstanceModel[] = [
|
||||
{ name: 'application-new-1', environmentId: fakeEnvironmentList[0].id, createdAt: '2018-09-21T12:31:39.000Z', status: 'Running', theme: 'theme-2', icon: 'favorite_border' },
|
||||
{ name: 'application-new-2', environmentId: fakeEnvironmentList[1].id,createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending', theme: 'theme-2', icon: 'favorite_border' },
|
||||
{ name: 'application-new-3', environmentId: fakeEnvironmentList[2].id,createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending' }
|
||||
];
|
||||
|
||||
@@ -27,7 +27,6 @@ export interface ApplicationInstanceModel {
|
||||
description?: string;
|
||||
connectors?: any;
|
||||
descriptor?: Descriptor;
|
||||
environmentId?: string;
|
||||
}
|
||||
|
||||
export interface Descriptor {
|
||||
|
||||
@@ -20,10 +20,9 @@ import { throwError } from 'rxjs';
|
||||
import { setupTestBed, AppConfigService, AlfrescoApiService, CoreTestingModule } from '@alfresco/adf-core';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { AppsProcessCloudService } from './apps-process-cloud.service';
|
||||
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from '../mock/app-model.mock';
|
||||
import { fakeApplicationInstance } from '../mock/app-model.mock';
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { fakeEnvironmentList } from '../../common/mock/environment.mock';
|
||||
|
||||
describe('AppsProcessCloudService', () => {
|
||||
|
||||
@@ -104,15 +103,4 @@ describe('AppsProcessCloudService', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should return label with application name', () => {
|
||||
const applicationLabel = service.getApplicationLabel(fakeApplicationInstance[0]);
|
||||
expect(applicationLabel).toBe('application-new-1');
|
||||
});
|
||||
|
||||
it('should return label with application name and environment name', () => {
|
||||
const applicationLabel = service.getApplicationLabel(fakeApplicationInstanceWithEnvironment[0], fakeEnvironmentList);
|
||||
expect(applicationLabel).toBe('application-new-1 (test-env-name-1)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ import { map, catchError } from 'rxjs/operators';
|
||||
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
|
||||
import { Oauth2Auth } from '@alfresco/js-api';
|
||||
import { ApplicationInstanceModel } from '../models/application-instance.model';
|
||||
import { Environment } from '../../common/interface/environment.interface';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AppsProcessCloudService {
|
||||
@@ -59,16 +58,6 @@ export class AppsProcessCloudService {
|
||||
this.deployedApps = apps;
|
||||
}
|
||||
|
||||
getApplicationLabel(application: ApplicationInstanceModel, environmentList?: Environment[]): string {
|
||||
const envName = environmentList?.find((env: Environment) => env.id === application.environmentId)?.name;
|
||||
|
||||
if (application.environmentId && environmentList && envName) {
|
||||
return `${application.name} (${envName})`;
|
||||
} else {
|
||||
return application.name;
|
||||
}
|
||||
}
|
||||
|
||||
private getApplicationsByStatus(status: string, role?: string): Observable<ApplicationInstanceModel[]> {
|
||||
if (status === '') {
|
||||
return of([]);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
||||
@@ -1,21 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface Environment {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './environment.interface';
|
||||
@@ -1,33 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Environment } from '../interface/environment.interface';
|
||||
|
||||
export const fakeEnvironmentList: Environment[] = [
|
||||
{
|
||||
id: 'test-env-1',
|
||||
name: 'test-env-name-1'
|
||||
},
|
||||
{
|
||||
id: 'test-env-2',
|
||||
name: 'test-env-name-2'
|
||||
},
|
||||
{
|
||||
id: 'test-env-3',
|
||||
name: 'test-env-name-3'
|
||||
}
|
||||
];
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './interface/index';
|
||||
@@ -962,8 +962,10 @@ describe('FormCloudComponent', () => {
|
||||
expect(formComponent.isOutcomeButtonEnabled(startProcessOutcome)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should raise [executeOutcome] event for formService', async () => {
|
||||
spyOn(formComponent.executeOutcome, 'emit');
|
||||
it('should raise [executeOutcome] event for formService', (done) => {
|
||||
formComponent.executeOutcome.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
const outcome = new FormOutcomeModel(new FormModel(), {
|
||||
id: FormCloudComponent.CUSTOM_OUTCOME_ID,
|
||||
@@ -972,10 +974,6 @@ describe('FormCloudComponent', () => {
|
||||
|
||||
formComponent.form = new FormModel();
|
||||
formComponent.onOutcomeClicked(outcome);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(formComponent.executeOutcome.emit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should refresh form values when data is changed', (done) => {
|
||||
|
||||
@@ -33,8 +33,7 @@ import {
|
||||
FormValues,
|
||||
FormModel,
|
||||
ContentLinkModel,
|
||||
UploadWidgetContentLinkModel,
|
||||
FormEvent
|
||||
UploadWidgetContentLinkModel
|
||||
} from '@alfresco/adf-core';
|
||||
import { FormCloudService } from '../services/form-cloud.service';
|
||||
import { TaskVariableCloud } from '../models/task-variable-cloud.model';
|
||||
@@ -120,7 +119,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
if (content instanceof UploadWidgetContentLinkModel) {
|
||||
this.form.setNodeIdValueForViewersLinkedToUploadWidget(content);
|
||||
this.onFormDataRefreshed(this.form);
|
||||
this.formService.formDataRefreshed.next(new FormEvent(this.form));
|
||||
} else {
|
||||
this.formContentClicked.emit(content);
|
||||
}
|
||||
|
||||
+32
-29
@@ -47,29 +47,31 @@ describe('FormDefinitionCloudComponent', () => {
|
||||
getFormsSpy = spyOn(service, 'getStandAloneTaskForms').and.returnValue(of([{ id: 'fake-form', name: 'fakeForm' } as any]));
|
||||
});
|
||||
|
||||
it('should load the forms by default', async () => {
|
||||
it('should load the forms by default', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
|
||||
clickMatSelect.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options[0].nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE');
|
||||
expect(options[1].nativeElement.innerText.trim()).toBe('fakeForm');
|
||||
expect(getFormsSpy).toHaveBeenCalled();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
|
||||
clickMatSelect.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options[0].nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE');
|
||||
expect(options[1].nativeElement.innerText.trim()).toBe('fakeForm');
|
||||
expect(getFormsSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load only None option when no forms exist', async () => {
|
||||
it('should load only None option when no forms exist', () => {
|
||||
getFormsSpy.and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
|
||||
clickMatSelect.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect((options).length).toBe(1);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
|
||||
clickMatSelect.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect((options).length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not preselect any form by default', () => {
|
||||
@@ -79,17 +81,18 @@ describe('FormDefinitionCloudComponent', () => {
|
||||
expect(formInput.nodeValue).toBeNull();
|
||||
});
|
||||
|
||||
it('should display the name of the form that is selected', async () => {
|
||||
it('should display the name of the form that is selected', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
|
||||
clickMatSelect.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
options[1].triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
const selected = fixture.debugElement.query(By.css('mat-select'));
|
||||
const selectedValue = ((selected).nativeElement.innerText);
|
||||
expect(selectedValue.trim()).toBe('fakeForm');
|
||||
fixture.whenStable().then(() => {
|
||||
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
|
||||
clickMatSelect.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
options[1].triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
const selected = fixture.debugElement.query(By.css('mat-select'));
|
||||
const selectedValue = ((selected).nativeElement.innerText);
|
||||
expect(selectedValue.trim()).toBe('fakeForm');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+34
-43
@@ -57,8 +57,7 @@ import {
|
||||
allSourceParamsWithRelativePath,
|
||||
fakeLocalPhysicalRecordResponse,
|
||||
displayableCMParams,
|
||||
fakeLocalPngHavingCMProperties,
|
||||
mockMyNodeId
|
||||
fakeLocalPngHavingCMProperties
|
||||
} from '../../../mocks/attach-file-cloud-widget.mock';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
@@ -183,7 +182,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
|
||||
it('should be able to attach files coming from content selector', async () => {
|
||||
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], contentSourceParam);
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeId);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
clickOnAttachFileWidget('attach-file-alfresco');
|
||||
@@ -220,12 +218,12 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should be able to set label property for Attach File widget', async () => {
|
||||
it('should be able to set label property for Attach File widget', () => {
|
||||
createUploadWidgetField(new FormModel(), 'attach-file', [], onlyLocalParams, false, 'Label', true);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(element.querySelector('label').innerText).toEqual('Label');
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('label').innerText).toEqual('Label');
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset the custom models when the component gets destroyed', () => {
|
||||
@@ -377,7 +375,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
it('Should set default user alias (-my-) as rootNodeId if destinationFolderPath contains wrong alias and single upload for Alfresco Content + Locale', async () => {
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId);
|
||||
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], allSourceWithWrongAliasParams, false);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -390,7 +387,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
it('Should set default user alias (-my-) as rootNodeId if destinationFolderPath contains wrong alias and multiple upload for Alfresco Content + Locale', async () => {
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId);
|
||||
|
||||
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], allSourceWithWrongAliasParams, true);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -403,7 +400,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
it('Should set default user alias (-my-) as rootNodeId if destinationFolderPath does not have alias for Alfresco Content + Locale', async () => {
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId);
|
||||
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], allSourceWithNoAliasParams, true);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -466,7 +462,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
it('Should be able to set default user alias (-my-) as rootNodeId if the nodeId of the alias is not fetched from the api', async () => {
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockMyNodeId);
|
||||
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [], contentSourceParam, false);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -548,11 +543,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should remove file when remove is clicked', async () => {
|
||||
it('should remove file when remove is clicked', (done) => {
|
||||
fixture.detectChanges();
|
||||
const menuButton = fixture.debugElement.query(By.css('#file-fake-properties-option-menu')).nativeElement as HTMLButtonElement;
|
||||
menuButton.click();
|
||||
@@ -560,8 +551,10 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
const removeOption = fixture.debugElement.query(By.css('#file-fake-properties-remove')).nativeElement as HTMLButtonElement;
|
||||
removeOption.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenRenderingDone();
|
||||
expect(element.querySelector('#file-fake-properties-icon')).toBeNull();
|
||||
fixture.whenRenderingDone().then(() => {
|
||||
expect(element.querySelector('#file-fake-properties-icon')).toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should download file when download is clicked', (done) => {
|
||||
@@ -619,31 +612,31 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
expect(updateFormSpy).toHaveBeenCalledWith(expectedValues);
|
||||
});
|
||||
|
||||
it('should display the default menu options if no options are provided', async () => {
|
||||
it('should display the default menu options if no options are provided', () => {
|
||||
widget.field.params = onlyLocalParams;
|
||||
const inputDebugElement = fixture.debugElement.query(
|
||||
By.css('#attach-file-alfresco')
|
||||
);
|
||||
inputDebugElement.triggerEventHandler('change', {
|
||||
target: { files: [fakeLocalPngAnswer] }
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const inputDebugElement = fixture.debugElement.query(
|
||||
By.css('#attach-file-attach')
|
||||
);
|
||||
inputDebugElement.triggerEventHandler('change', {
|
||||
target: { files: [fakeLocalPngAnswer] }
|
||||
});
|
||||
fixture.detectChanges();
|
||||
const menuButton = fixture.debugElement.query(By.css('#file-1155-option-menu')).nativeElement as HTMLButtonElement;
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const showOption = fixture.debugElement.query(By.css('#file-1155-show-file')).nativeElement as HTMLButtonElement;
|
||||
const downloadOption = fixture.debugElement.query(By.css('#file-1155-download-file')).nativeElement as HTMLButtonElement;
|
||||
const retrieveMetadataOption = fixture.debugElement.query(By.css('#file-1155-retrieve-file-metadata')).nativeElement as HTMLButtonElement;
|
||||
const removeOption = fixture.debugElement.query(By.css('#file-1155-remove')).nativeElement as HTMLButtonElement;
|
||||
|
||||
expect(showOption).not.toBeNull();
|
||||
expect(downloadOption).not.toBeNull();
|
||||
expect(retrieveMetadataOption).toBeNull();
|
||||
expect(removeOption).not.toBeNull();
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const menuButton = fixture.debugElement.query(By.css('#file-fake-properties-option-menu')).nativeElement as HTMLButtonElement;
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const showOption = fixture.debugElement.query(By.css('#file-fake-properties-show-file'));
|
||||
const downloadOption = fixture.debugElement.query(By.css('#file-fake-properties-download-file'));
|
||||
const retrieveMetadataOption = fixture.debugElement.query(By.css('#file-fake-properties-retrieve-file-metadata'));
|
||||
const removeOption = fixture.debugElement.query(By.css('#file-fake-properties-remove'));
|
||||
|
||||
expect(showOption).not.toBeNull();
|
||||
expect(downloadOption).not.toBeNull();
|
||||
expect(retrieveMetadataOption).toBeNull();
|
||||
expect(removeOption).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -718,7 +711,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
it('should have been called on attach file when value was empty', async () => {
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeId);
|
||||
clickOnAttachFileWidget('attach-file-alfresco');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -729,7 +721,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
it('should not be called on attach file when has a file previously', async () => {
|
||||
spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeId);
|
||||
widget.field.value = [fakeMinimalNode];
|
||||
|
||||
clickOnAttachFileWidget('attach-file-alfresco');
|
||||
|
||||
+1
-3
@@ -19,7 +19,6 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FileViewerWidgetComponent } from './file-viewer.widget';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormModel, FormService, FormFieldModel } from '@alfresco/adf-core';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
|
||||
describe('FileViewerWidgetComponent', () => {
|
||||
const fakeForm = new FormModel();
|
||||
@@ -48,8 +47,7 @@ describe('FileViewerWidgetComponent', () => {
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
declarations: [ FileViewerWidgetComponent ],
|
||||
providers: [ { provide: FormService, useValue: formServiceStub } ],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
providers: [ { provide: FormService, useValue: formServiceStub } ]
|
||||
});
|
||||
|
||||
formServiceStub = TestBed.inject(FormService);
|
||||
|
||||
@@ -292,8 +292,6 @@ export const mockNodeId = new Promise<string>((resolve) => {
|
||||
resolve('mock-node-id');
|
||||
});
|
||||
|
||||
export const mockMyNodeId = Promise.resolve('-my-');
|
||||
|
||||
export const mockNodeIdBasedOnStringVariableValue = new Promise<string>((resolve) => {
|
||||
resolve('mock-string-value-node-id');
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ export class TaskQueryCloudRequestModel {
|
||||
appName: string;
|
||||
appVersion?: string;
|
||||
assignee?: string;
|
||||
environmentId?: string;
|
||||
claimedDate?: string;
|
||||
createdDate?: Date;
|
||||
createdFrom?: string;
|
||||
@@ -57,7 +56,6 @@ export class TaskQueryCloudRequestModel {
|
||||
this.appName = obj.appName;
|
||||
this.appVersion = obj.appVersion;
|
||||
this.assignee = obj.assignee;
|
||||
this.environmentId = obj.environmentId;
|
||||
this.claimedDate = obj.claimedDate;
|
||||
this.createdDate = obj.createdDate;
|
||||
this.createdFrom = obj.createdFrom;
|
||||
|
||||
+11
-11
@@ -30,7 +30,7 @@ import { ProcessFiltersCloudModule } from '../process-filters-cloud.module';
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
|
||||
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from './../../../app/mock/app-model.mock';
|
||||
import { fakeApplicationInstance } from './../../../app/mock/app-model.mock';
|
||||
import moment from 'moment';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
@@ -41,7 +41,6 @@ import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
|
||||
import { mockAppVersions } from '../mock/process-filters-cloud.mock';
|
||||
import { DATE_FORMAT_CLOUD } from '../../../models/date-format-cloud.model';
|
||||
import { fakeEnvironmentList } from '../../../common/mock/environment.mock';
|
||||
|
||||
describe('EditProcessFilterCloudComponent', () => {
|
||||
let component: EditProcessFilterCloudComponent;
|
||||
@@ -444,6 +443,16 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should have floating labels when values are present', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const inputLabelsNodes = document.querySelectorAll('mat-form-field');
|
||||
inputLabelsNodes.forEach(labelNode => {
|
||||
expect(labelNode.getAttribute('ng-reflect-float-label')).toBe('auto');
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to filter filterProperties when input is defined', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -1162,13 +1171,4 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
expect(component.initiatorOptions).toEqual([{ username: 'user1' }, { username: 'user2' }]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add environment name to each application selector option label', () => {
|
||||
component.environmentList = fakeEnvironmentList;
|
||||
component.environmentId = fakeEnvironmentList[0].id;
|
||||
|
||||
getRunningApplicationsSpy.and.returnValue(of(fakeApplicationInstanceWithEnvironment));
|
||||
component.getRunningApplications();
|
||||
expect(component.applicationNames[0].label).toBe('application-new-1 (test-env-name-1)');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-19
@@ -30,7 +30,6 @@ import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud
|
||||
import { ProcessCloudService } from '../../services/process-cloud.service';
|
||||
import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud-filter.model';
|
||||
import { IdentityUserModel } from '../../../people/models/identity-user.model';
|
||||
import { Environment } from '../../../common/interface/environment.interface';
|
||||
|
||||
export const PROCESS_FILTER_ACTION_SAVE = 'save';
|
||||
export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs';
|
||||
@@ -77,14 +76,6 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
|
||||
@Input()
|
||||
actions = DEFAULT_ACTIONS;
|
||||
|
||||
/** Environment ID of the application. */
|
||||
@Input()
|
||||
environmentId: string;
|
||||
|
||||
/** List of environments. */
|
||||
@Input()
|
||||
environmentList: Environment[] = [];
|
||||
|
||||
/** Toggles editing of process filter actions. */
|
||||
@Input()
|
||||
showFilterActions = true;
|
||||
@@ -127,10 +118,6 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
|
||||
this.id = value.id;
|
||||
}
|
||||
|
||||
if (value?.environmentId) {
|
||||
this.environmentId = value.environmentId;
|
||||
}
|
||||
|
||||
this.processFilterProperties = this.createAndFilterProperties();
|
||||
this.processFilterActions = this.createAndFilterActions();
|
||||
|
||||
@@ -413,7 +400,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
|
||||
.subscribe((applications) => {
|
||||
if (applications && applications.length > 0) {
|
||||
applications.map((application) => {
|
||||
this.applicationNames.push({ label: this.appsProcessCloudService.getApplicationLabel(application, this.environmentList), value: application.name });
|
||||
this.applicationNames.push({ label: application.name, value: application.name });
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -638,11 +625,6 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
|
||||
key: 'id',
|
||||
value: 'id'
|
||||
},
|
||||
{
|
||||
label: 'EnvironmentId',
|
||||
key: 'environmentId',
|
||||
value: 'environmentId'
|
||||
},
|
||||
{
|
||||
label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.PROCESS_NAME',
|
||||
key: 'name',
|
||||
|
||||
+13
-25
@@ -1,29 +1,17 @@
|
||||
|
||||
|
||||
<mat-nav-list class="adf-process-filters" *ngIf="filters$ | async as filterList; else loading">
|
||||
<button
|
||||
*ngFor="let filter of filterList"
|
||||
mat-list-item
|
||||
(click)="onFilterClick(filter)"
|
||||
[attr.aria-label]="filter.name | translate"
|
||||
[id]="filter.id"
|
||||
[attr.data-automation-id]="filter.key + '_filter'"
|
||||
[class.adf-active]="currentFilter === filter"
|
||||
>
|
||||
<div class="adf-process-filters__entry">
|
||||
<adf-icon
|
||||
data-automation-id="adf-filter-icon"
|
||||
*ngIf="showIcons"
|
||||
[value]="filter.icon">
|
||||
</adf-icon>
|
||||
<span
|
||||
data-automation-id="adf-filter-label"
|
||||
class="adf-filter-action-button__label">
|
||||
{{ filter.name | translate }}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</mat-nav-list>
|
||||
<ng-container *ngIf="filters$ | async as filterList; else loading">
|
||||
<div *ngFor="let filter of filterList" class="adf-filters__entry" [class.adf-active]="currentFilter === filter">
|
||||
<button (click)="onFilterClick(filter)"
|
||||
[attr.aria-label]="filter.name | translate"
|
||||
[id]="filter.id"
|
||||
[attr.data-automation-id]="filter.key + '_filter'"
|
||||
mat-button
|
||||
class="adf-filter-action-button adf-full-width" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<adf-icon data-automation-id="adf-filter-icon" *ngIf="showIcons" [value]="filter.icon"></adf-icon>
|
||||
<span data-automation-id="adf-filter-label" class="adf-filter-action-button__label">{{ filter.name | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #loading>
|
||||
<ng-container>
|
||||
<div class="adf-app-list-spinner">
|
||||
|
||||
+20
-16
@@ -1,23 +1,27 @@
|
||||
.adf-process-filters {
|
||||
margin-right: calc(-1 * var(--adf-theme-spacing));
|
||||
.adf {
|
||||
&-filters__entry {
|
||||
padding: 12px 0 !important;
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
font-size: var(--theme-body-1-font-size) !important;
|
||||
font-weight: bold;
|
||||
opacity: 1;
|
||||
|
||||
&__entry {
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
color: var(--adf-theme-foreground-text-color-054);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
gap: var(--adf-theme-spacing);
|
||||
.adf-full-width {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.adf-filter-action-button .adf-filter-action-button__label {
|
||||
padding-left: 20px;
|
||||
margin: 0 8px !important;
|
||||
}
|
||||
|
||||
&.adf-active,
|
||||
&:hover {
|
||||
color: var(--theme-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.adf-active {
|
||||
.adf-process-filters__entry {
|
||||
color: var(--theme-primary-color);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ describe('ProcessFiltersCloudComponent', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const filters = fixture.debugElement.queryAll(By.css('.adf-process-filters__entry'));
|
||||
const filters = fixture.debugElement.queryAll(By.css('.adf-filters__entry'));
|
||||
expect(component.filters.length).toBe(3);
|
||||
expect(filters.length).toBe(3);
|
||||
expect(filters[0].nativeElement.innerText).toContain('FakeAllProcesses');
|
||||
|
||||
-2
@@ -46,7 +46,6 @@ export class ProcessFilterCloudModel {
|
||||
startedDateType: DateCloudFilterType;
|
||||
suspendedDateType: DateCloudFilterType;
|
||||
completedDate: Date;
|
||||
environmentId?: string;
|
||||
|
||||
private dateRangeFilterService = new DateRangeFilterService();
|
||||
private _completedFrom: string;
|
||||
@@ -61,7 +60,6 @@ export class ProcessFilterCloudModel {
|
||||
this.id = obj.id || Math.random().toString(36).substring(2, 9);
|
||||
this.name = obj.name || null;
|
||||
this.key = obj.key || null;
|
||||
this.environmentId = obj.environmentId;
|
||||
this.icon = obj.icon || null;
|
||||
this.index = obj.index || null;
|
||||
this.appName = obj.appName || obj.appName === '' ? obj.appName : null;
|
||||
|
||||
+1
-6
@@ -61,12 +61,7 @@ export class ProcessFilterCloudService {
|
||||
value = value || {};
|
||||
const result = {
|
||||
appName: appName || value['appName'],
|
||||
id: id || value['id'],
|
||||
...(
|
||||
value['environmentId'] && {
|
||||
environmentId: value['environmentId']
|
||||
}
|
||||
)
|
||||
id: id || value['id']
|
||||
};
|
||||
|
||||
for (const prop of filterProperties) {
|
||||
|
||||
+23
-36
@@ -16,15 +16,13 @@
|
||||
*/
|
||||
|
||||
import { Component, SimpleChange, ViewChild } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {
|
||||
AppConfigService,
|
||||
ColumnsSelectorComponent,
|
||||
CustomEmptyContentTemplateDirective,
|
||||
DataColumn,
|
||||
DataRowEvent,
|
||||
DataTableModule,
|
||||
getDataColumnMock,
|
||||
ObjectDataRow,
|
||||
setupTestBed
|
||||
@@ -41,9 +39,6 @@ import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
|
||||
import { PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -64,6 +59,19 @@ class CustomTaskListComponent {
|
||||
processListCloud: ProcessListCloudComponent;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<adf-cloud-process-list>
|
||||
<adf-custom-empty-content-template>
|
||||
<p id="custom-id">TEST</p>
|
||||
</adf-custom-empty-content-template>
|
||||
</adf-cloud-process-list>
|
||||
`
|
||||
})
|
||||
|
||||
class EmptyTemplateComponent {
|
||||
}
|
||||
|
||||
describe('ProcessListCloudComponent', () => {
|
||||
let component: ProcessListCloudComponent;
|
||||
let fixture: ComponentFixture<ProcessListCloudComponent>;
|
||||
@@ -550,33 +558,14 @@ describe('ProcessListCloudComponent', () => {
|
||||
|
||||
describe('Creating an empty custom template - EmptyTemplateComponent', () => {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<adf-cloud-process-list #processListCloud>
|
||||
<adf-custom-empty-content-template>
|
||||
<p id="custom-id">TEST</p>
|
||||
</adf-custom-empty-content-template>
|
||||
</adf-cloud-process-list>
|
||||
`
|
||||
})
|
||||
|
||||
class EmptyTemplateComponent {
|
||||
@ViewChild(ProcessListCloudComponent)
|
||||
processListCloud: ProcessListCloudComponent;
|
||||
}
|
||||
|
||||
let fixtureEmpty: ComponentFixture<EmptyTemplateComponent>;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
HttpClientModule,
|
||||
NoopAnimationsModule,
|
||||
DataTableModule,
|
||||
MatProgressSpinnerModule
|
||||
ProcessServiceCloudTestingModule
|
||||
],
|
||||
providers: [{ provide: PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, useExisting: LocalPreferenceCloudService }],
|
||||
declarations: [EmptyTemplateComponent, ProcessListCloudComponent, CustomEmptyContentTemplateDirective]
|
||||
declarations: [EmptyTemplateComponent]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -588,16 +577,14 @@ describe('ProcessListCloudComponent', () => {
|
||||
fixtureEmpty.destroy();
|
||||
});
|
||||
|
||||
it('should render the custom template', async () => {
|
||||
it('should render the custom template', fakeAsync((done) => {
|
||||
const emptyList = {list: {entries: []}};
|
||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList));
|
||||
fixtureEmpty.componentInstance.processListCloud.isLoading = false;
|
||||
fixtureEmpty.detectChanges();
|
||||
await fixtureEmpty.whenStable();
|
||||
|
||||
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
|
||||
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
|
||||
|
||||
});
|
||||
component.success.subscribe(() => {
|
||||
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
|
||||
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
|
||||
done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
-5
@@ -67,10 +67,6 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
|
||||
@Input()
|
||||
id: string = '';
|
||||
|
||||
/** Filter the processes to display only the ones with this environment ID. */
|
||||
@Input()
|
||||
environmentId: string = '';
|
||||
|
||||
/** Filter the processes to display only the ones with this name. */
|
||||
@Input()
|
||||
name: string = '';
|
||||
@@ -459,7 +455,6 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
|
||||
skipCount: this.skipCount,
|
||||
initiator: this.initiator,
|
||||
id: this.id,
|
||||
environmentId: this.environmentId,
|
||||
name: this.name,
|
||||
processDefinitionId: this.processDefinitionId,
|
||||
processDefinitionName: this.processDefinitionName,
|
||||
|
||||
-2
@@ -22,7 +22,6 @@ export class ProcessQueryCloudRequestModel {
|
||||
appVersion?: number | string;
|
||||
initiator?: null;
|
||||
id?: string;
|
||||
environmentId?: string;
|
||||
name?: string;
|
||||
processDefinitionId?: string;
|
||||
processDefinitionName?: string;
|
||||
@@ -51,7 +50,6 @@ export class ProcessQueryCloudRequestModel {
|
||||
this.appVersion = obj.appVersion;
|
||||
this.initiator = obj.initiator;
|
||||
this.id = obj.id;
|
||||
this.environmentId = obj.environmentId;
|
||||
this.name = obj.name;
|
||||
this.processDefinitionId = obj.processDefinitionId;
|
||||
this.processDefinitionName = obj.processDefinitionName;
|
||||
|
||||
+270
-247
@@ -61,14 +61,13 @@ describe('StartProcessCloudComponent', () => {
|
||||
|
||||
const firstChange = new SimpleChange(undefined, 'myApp', true);
|
||||
|
||||
const selectOptionByName = async (name: string) => {
|
||||
const selectOptionByName = (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 options: any = fixture.debugElement.queryAll(By.css('.mat-option-text'));
|
||||
const currentOption = options.find((option: DebugElement) => option.nativeElement.innerHTML.trim() === name);
|
||||
|
||||
if (currentOption) {
|
||||
currentOption.nativeElement.click();
|
||||
@@ -105,8 +104,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
fixture = TestBed.createComponent(StartProcessCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions');
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm');
|
||||
getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(of(fakeProcessDefinitions));
|
||||
spyOn(processService, 'updateProcess').and.returnValue(of());
|
||||
startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(of(fakeProcessInstance));
|
||||
getStartEventFormStaticValuesMappingSpy = spyOn(processService, 'getStartEventFormStaticValuesMapping').and.returnValue(of([]));
|
||||
@@ -120,9 +118,8 @@ describe('StartProcessCloudComponent', () => {
|
||||
describe('start a process without start form', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.name = 'My formless new process';
|
||||
component.name = 'My new process';
|
||||
component.appName = 'myApp';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
fixture.detectChanges();
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
@@ -140,25 +137,28 @@ describe('StartProcessCloudComponent', () => {
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
fixture.detectChanges();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.isProcessFormValid()).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.isProcessFormValid()).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create a process instance if the selection is valid', async () => {
|
||||
it('should create a process instance if the selection is valid', fakeAsync(() => {
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.name = 'My new process';
|
||||
component.processDefinitionName = 'process';
|
||||
await selectOptionByName('processwithoutform2');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
selectOptionByName('process');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[1])).name);
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.isProcessFormValid()).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[1])).name);
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.isProcessFormValid()).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should have start button disabled when no process is selected', async () => {
|
||||
component.name = '';
|
||||
@@ -206,7 +206,10 @@ describe('StartProcessCloudComponent', () => {
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
expect(component.resolvedValues).toEqual(staticInputs.concat(values));
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.resolvedValues).toEqual(staticInputs.concat(values));
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -214,11 +217,6 @@ describe('StartProcessCloudComponent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.name = 'My new process with form';
|
||||
component.appName = 'startformwithoutupload';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
fixture.detectChanges();
|
||||
const change = new SimpleChange(null, 'startformwithoutupload', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
component.values = [{
|
||||
id: '1',
|
||||
type: 'string',
|
||||
@@ -241,113 +239,162 @@ describe('StartProcessCloudComponent', () => {
|
||||
this['value'] = value;
|
||||
}
|
||||
}];
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be able to start a process with a valid form', async () => {
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
it('should be able to start a process with a valid form', fakeAsync(() => {
|
||||
component.processDefinitionName = 'processwithform';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
fixture.detectChanges();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should NOT be able to start a process with a form NOT valid', async () => {
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartFormNotValid));
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
fixture.detectChanges();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(false);
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should be able to start a process with a prefilled valid form', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
expect(firstNameEl.value).toEqual('FakeName');
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
expect(lastNameEl.value).toEqual('FakeLastName');
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should NOT be able to start a process with a prefilled NOT valid form', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartFormNotValid));
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(formDefinitionSpy).toHaveBeenCalled();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
expect(firstNameEl.value).toEqual('FakeName');
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
expect(lastNameEl.value).toEqual('FakeLastName');
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(false);
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should display enabled start process button if the selection is valid', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2')));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithoutform2');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
expect(component.isProcessFormValid()).toBe(true);
|
||||
});
|
||||
|
||||
it('should have start button enabled when default values are set', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2')));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
|
||||
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
tick();
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should NOT be able to start a process with a form NOT valid', fakeAsync(() => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
|
||||
fixture.detectChanges();
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartFormNotValid));
|
||||
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
|
||||
tick();
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(false);
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be able to start a process with a prefilled valid form', fakeAsync(() => {
|
||||
component.processDefinitionName = 'processwithform';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
|
||||
fixture.detectChanges();
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
|
||||
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
|
||||
tick();
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
expect(firstNameEl.value).toEqual('FakeName');
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
expect(lastNameEl.value).toEqual('FakeLastName');
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(true);
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should NOT be able to start a process with a prefilled NOT valid form', fakeAsync(() => {
|
||||
component.processDefinitionName = 'processwithform';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
|
||||
fixture.detectChanges();
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartFormNotValid));
|
||||
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
|
||||
tick();
|
||||
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
|
||||
typeValueInto('#processDefinitionName', 'processwithform');
|
||||
fixture.detectChanges();
|
||||
tick(4500);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(formDefinitionSpy).toHaveBeenCalled();
|
||||
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
|
||||
expect(firstNameEl).toBeDefined();
|
||||
expect(firstNameEl.value).toEqual('FakeName');
|
||||
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
|
||||
expect(lastNameEl).toBeDefined();
|
||||
expect(lastNameEl.value).toEqual('FakeLastName');
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(component.formCloud.isValid).toBe(false);
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display enabled start process button if the selection is valid', fakeAsync(() => {
|
||||
component.name = 'testFormWithProcess';
|
||||
component.processDefinitionName = 'processwithoutform2';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
|
||||
fixture.detectChanges();
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
|
||||
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
expect(component.formCloud.isValid).toBe(true);
|
||||
expect(component.isProcessFormValid()).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should have start button enabled when default values are set', fakeAsync(() => {
|
||||
component.name = 'testFormWithProcess';
|
||||
component.processDefinitionName = 'processwithoutform2';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
|
||||
fixture.detectChanges();
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
|
||||
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(startBtn.disabled).toBe(false);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('process definitions list', () => {
|
||||
@@ -355,60 +402,55 @@ describe('StartProcessCloudComponent', () => {
|
||||
beforeEach(() => {
|
||||
component.name = 'My new process';
|
||||
component.appName = 'myApp';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
fixture.detectChanges();
|
||||
const change = new SimpleChange(null, 'MyApp', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should call service to fetch process definitions with appId', async () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith('MyApp');
|
||||
it('should call service to fetch process definitions with appId', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith('myApp');
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the correct number of processes in the select list', async () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
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 correct number of processes in the select list', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
const selectElement = fixture.nativeElement.querySelector('mat-select');
|
||||
expect(selectElement.children.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the option def details', async () => {
|
||||
it('should display the option def details', () => {
|
||||
component.processDefinitionList = fakeProcessDefinitions;
|
||||
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();
|
||||
fixture.whenStable().then(() => {
|
||||
const selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger');
|
||||
const optionElement = fixture.nativeElement.querySelectorAll('mat-option');
|
||||
selectElement.click();
|
||||
expect(selectElement).not.toBeNull();
|
||||
expect(selectElement).toBeDefined();
|
||||
expect(optionElement).not.toBeNull();
|
||||
expect(optionElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the key when the processDefinition name is empty or null', async () => {
|
||||
it('should display the key when the processDefinition name is empty or null', () => {
|
||||
component.processDefinitionList = fakeNoNameProcessDefinitions;
|
||||
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');
|
||||
fixture.whenStable().then(() => {
|
||||
const selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger');
|
||||
const optionElement = fixture.nativeElement.querySelectorAll('mat-option');
|
||||
selectElement.click();
|
||||
expect(selectElement).not.toBeNull();
|
||||
expect(selectElement).toBeDefined();
|
||||
expect(optionElement).not.toBeNull();
|
||||
expect(optionElement[0].textContent).toBe('NewProcess 1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should indicate an error to the user if process defs cannot be loaded', async () => {
|
||||
getDefinitionsSpy.and.returnValue(throwError({}));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(throwError({}));
|
||||
const change = new SimpleChange('myApp', 'myApp1', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
|
||||
@@ -420,7 +462,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
});
|
||||
|
||||
it('should show no process available message when no process definition is loaded', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of([]));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([]));
|
||||
const change = new SimpleChange('myApp', 'myApp1', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
|
||||
@@ -433,7 +475,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
});
|
||||
|
||||
it('should select automatically the processDefinition if the app contain only one', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
|
||||
const change = new SimpleChange('myApp', 'myApp1', true);
|
||||
component.ngOnChanges({ appName: change });
|
||||
|
||||
@@ -443,30 +485,29 @@ describe('StartProcessCloudComponent', () => {
|
||||
expect(component.processForm.controls['processDefinition'].value).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[0])).name);
|
||||
});
|
||||
|
||||
it('should select automatically the form when processDefinition is selected as default', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]]));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
it('should select automatically the form when processDefinition is selected as default', fakeAsync(() => {
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
|
||||
const change = new SimpleChange('myApp', 'myApp1', true);
|
||||
component.ngOnInit();
|
||||
component.ngOnChanges({ appName: change });
|
||||
component.processForm.controls['processDefinition'].setValue('process');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
await selectOptionByName('processwithform');
|
||||
tick(3000);
|
||||
component.processDefinitionName = fakeProcessDefinitions[0].name;
|
||||
component.setProcessDefinitionOnForm(fakeProcessDefinitions[0].name);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
tick(3000);
|
||||
|
||||
component.processDefinitionName = fakeProcessDefinitions[2].name;
|
||||
component.setProcessDefinitionOnForm(fakeProcessDefinitions[2].name);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const processForm = fixture.nativeElement.querySelector('adf-cloud-form');
|
||||
expect(component.hasForm()).toBeTruthy();
|
||||
expect(processForm).not.toBeNull();
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
const processForm = fixture.nativeElement.querySelector('adf-cloud-form');
|
||||
expect(component.hasForm()).toBeTruthy();
|
||||
expect(processForm).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not select automatically any processDefinition if the app contain multiple process and does not have any processDefinition as input', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.appName = 'myApp';
|
||||
component.ngOnChanges({});
|
||||
|
||||
@@ -476,27 +517,23 @@ describe('StartProcessCloudComponent', () => {
|
||||
expect(component.processPayloadCloud.name).toBeNull();
|
||||
});
|
||||
|
||||
it('should select the right process when the processKey begins with the name', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
it('should select the right process when the processKey begins with the name', fakeAsync(() => {
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.name = 'My new process';
|
||||
component.processDefinitionName = 'process';
|
||||
component.appName = 'myApp';
|
||||
component.ngOnChanges({});
|
||||
selectOptionByName('process');
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).name);
|
||||
expect(component.processDefinitionCurrent.key).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).key);
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).name);
|
||||
expect(component.processDefinitionCurrent.key).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[3])).key);
|
||||
});
|
||||
}));
|
||||
|
||||
describe('dropdown', () => {
|
||||
|
||||
it('should hide the process dropdown button if showSelectProcessDropdown is false', async () => {
|
||||
fixture.detectChanges();
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.appName = 'myApp';
|
||||
component.showSelectProcessDropdown = false;
|
||||
component.ngOnChanges({});
|
||||
@@ -510,7 +547,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
|
||||
it('should show the process dropdown button if showSelectProcessDropdown is false', async () => {
|
||||
fixture.detectChanges();
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.appName = 'myApp';
|
||||
component.processDefinitionName = 'NewProcess 2';
|
||||
component.showSelectProcessDropdown = true;
|
||||
@@ -525,7 +562,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
|
||||
it('should show the process dropdown button by default', async () => {
|
||||
fixture.detectChanges();
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.appName = 'myApp';
|
||||
component.processDefinitionName = 'NewProcess 2';
|
||||
component.ngOnChanges({});
|
||||
@@ -544,27 +581,25 @@ describe('StartProcessCloudComponent', () => {
|
||||
const change = new SimpleChange('myApp', 'myApp1', false);
|
||||
|
||||
beforeEach(() => {
|
||||
component.name = 'My new process';
|
||||
component.appName = 'myApp';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should have floating labels for process name and type', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.ngOnChanges({ appName: change });
|
||||
component.appName = 'myApp';
|
||||
component.processDefinitionName = 'NewProcess 2';
|
||||
component.ngOnChanges({});
|
||||
|
||||
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);
|
||||
const inputLabelsNodes = document.querySelectorAll('.adf-start-process .adf-process-input-container');
|
||||
inputLabelsNodes.forEach(labelNode => {
|
||||
expect(labelNode.getAttribute('ng-reflect-float-label')).toBe('always');
|
||||
});
|
||||
});
|
||||
|
||||
it('should reload processes when appName input changed', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.ngOnChanges({ appName: firstChange });
|
||||
component.ngOnChanges({ appName: change });
|
||||
|
||||
@@ -575,7 +610,6 @@ describe('StartProcessCloudComponent', () => {
|
||||
});
|
||||
|
||||
it('should reload processes ONLY when appName input changed', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.ngOnChanges({ appName: firstChange });
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -587,30 +621,28 @@ describe('StartProcessCloudComponent', () => {
|
||||
});
|
||||
|
||||
it('should get current processDef', () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
expect(getDefinitionsSpy).toHaveBeenCalled();
|
||||
expect(component.processDefinitionList).toBe(fakeProcessDefinitions);
|
||||
});
|
||||
|
||||
it('should display the matching results in the dropdown as the user types down', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
it('should display the matching results in the dropdown as the user types down', fakeAsync(() => {
|
||||
component.processDefinitionList = fakeProcessDefinitions;
|
||||
component.ngOnInit();
|
||||
component.ngOnChanges({ appName: change });
|
||||
fixture.detectChanges();
|
||||
|
||||
typeValueInto('#processDefinitionName', 'process');
|
||||
component.processForm.controls['processDefinition'].setValue('process');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
tick(3000);
|
||||
expect(component.filteredProcesses.length).toEqual(4);
|
||||
|
||||
typeValueInto('#processDefinitionName', 'processwithfo');
|
||||
component.processForm.controls['processDefinition'].setValue('processwithfo');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
tick(3000);
|
||||
expect(component.filteredProcesses.length).toEqual(1);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display the process definion field as empty if are more than one process definition in the list', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
@@ -630,8 +662,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
fixture.detectChanges();
|
||||
component.name = 'NewProcess 1';
|
||||
component.appName = 'myApp';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({});
|
||||
});
|
||||
|
||||
it('should see start button', async () => {
|
||||
@@ -715,22 +746,19 @@ describe('StartProcessCloudComponent', () => {
|
||||
expect(startProcessSpy).toHaveBeenCalledWith(component.appName, payload);
|
||||
});
|
||||
|
||||
it('should call service with the correct parameters when variables are undefined and formCloud is defined', async () => {
|
||||
getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]]));
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
it('should call service with the correct parameters when variables are undefined and formCloud is defined', fakeAsync(() => {
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
|
||||
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
|
||||
const change = new SimpleChange('myApp', 'myApp1', true);
|
||||
component.ngOnInit();
|
||||
component.ngOnChanges({ appName: change });
|
||||
component.processForm.controls['processDefinition'].setValue('process');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
await selectOptionByName('processwithform');
|
||||
tick(3000);
|
||||
component.processDefinitionName = fakeProcessDefinitions[0].name;
|
||||
component.setProcessDefinitionOnForm(fakeProcessDefinitions[0].name);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
component.processDefinitionName = fakeProcessDefinitions[2].name;
|
||||
component.setProcessDefinitionOnForm(fakeProcessDefinitions[2].name);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
tick(3000);
|
||||
|
||||
const processForm = fixture.nativeElement.querySelector('adf-cloud-form');
|
||||
expect(component.hasForm()).toBeTruthy();
|
||||
@@ -738,7 +766,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
|
||||
const payload: ProcessPayloadCloud = new ProcessPayloadCloud({
|
||||
name: component.processInstanceName.value,
|
||||
processDefinitionKey: fakeProcessDefinitions[2].key,
|
||||
processDefinitionKey: fakeProcessDefinitions[0].key,
|
||||
variables: Object.assign({}, component.formCloud.values)
|
||||
});
|
||||
const startButton = fixture.debugElement.query(By.css('#button-start'));
|
||||
@@ -746,7 +774,7 @@ describe('StartProcessCloudComponent', () => {
|
||||
|
||||
startButton.triggerEventHandler('click', null);
|
||||
expect(startProcessSpy).toHaveBeenCalledWith(component.appName, payload);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should output start event when process started successfully', () => {
|
||||
const emitSpy = spyOn(component.success, 'emit');
|
||||
@@ -830,18 +858,16 @@ describe('StartProcessCloudComponent', () => {
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should emit processDefinitionSelection event when a process definition is selected', async () => {
|
||||
const emitSpy = spyOn(component.processDefinitionSelection, 'emit');
|
||||
it('should emit processDefinitionSelection event when a process definition is selected', (done) => {
|
||||
component.appName = 'myApp';
|
||||
formDefinitionSpy.and.returnValue(of(fakeStartForm));
|
||||
component.ngOnChanges({ appName: firstChange });
|
||||
|
||||
component.processDefinitionSelection.subscribe((processDefinition) => {
|
||||
expect(processDefinition).toEqual(fakeProcessDefinitions[0]);
|
||||
done();
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
component.processDefinitionName = 'processwithoutform1';
|
||||
selectOptionByName(fakeProcessDefinitions[0].name);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(emitSpy).toHaveBeenCalledOnceWith(fakeProcessDefinitions[0]);
|
||||
});
|
||||
|
||||
it('should wait for process definition to be loaded before showing the empty process definition message', () => {
|
||||
@@ -857,18 +883,17 @@ describe('StartProcessCloudComponent', () => {
|
||||
expect(noProcessElement).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should set the process name using the processName cloud pipe when a process definition gets selected', async () => {
|
||||
it('should set the process name using the processName cloud pipe when a process definition gets selected', () => {
|
||||
const processNameCloudPipe = TestBed.inject(ProcessNameCloudPipe);
|
||||
const processNamePipeTransformSpy = spyOn(processNameCloudPipe, 'transform').and.returnValue('fake-transformed-name');
|
||||
const expectedProcessInstanceDetails: ProcessInstanceCloud = { processDefinitionName: fakeProcessDefinitions[0].name };
|
||||
getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
|
||||
formDefinitionSpy.and.stub();
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
|
||||
component.appName = 'myApp';
|
||||
component.ngOnChanges({ appName: firstChange });
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
selectOptionByName(fakeProcessDefinitions[0].name);
|
||||
|
||||
expect(processNamePipeTransformSpy).toHaveBeenCalledWith(component.name, expectedProcessInstanceDetails);
|
||||
expect(component.processInstanceName.dirty).toBe(true);
|
||||
@@ -977,7 +1002,6 @@ describe('StartProcessCloudComponent', () => {
|
||||
beforeEach(() => {
|
||||
component.name = 'NewProcess 1';
|
||||
component.appName = 'myApp';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.ngOnChanges({ appName: firstChange });
|
||||
component.processDefinitionList = fakeProcessDefinitions;
|
||||
component.processDefinitionName = fakeProcessDefinitions[0].name;
|
||||
@@ -1016,7 +1040,6 @@ describe('StartProcessCloudComponent', () => {
|
||||
beforeEach(() => {
|
||||
component.name = 'NewProcess 1';
|
||||
component.appName = 'myApp';
|
||||
getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
|
||||
component.ngOnChanges({ appName: firstChange });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('PreferenceService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not fetch preferences if error occurred', (done) => {
|
||||
it('Should not fetch preferences if error occurred', () => {
|
||||
getInstanceSpy.and.returnValue(apiErrorMock);
|
||||
service.getPreferences('mock-app-name')
|
||||
.subscribe(
|
||||
@@ -89,7 +89,6 @@ describe('PreferenceService', () => {
|
||||
expect(error.state).toEqual(404);
|
||||
expect(error.stateText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -108,7 +107,7 @@ describe('PreferenceService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not fetch preference by key if error occurred', (done) => {
|
||||
it('Should not fetch preference by key if error occurred', () => {
|
||||
getInstanceSpy.and.returnValue(apiErrorMock);
|
||||
service.getPreferenceByKey('mock-app-name', 'mock-preference-key')
|
||||
.subscribe(
|
||||
@@ -117,7 +116,6 @@ describe('PreferenceService', () => {
|
||||
expect(error.state).toEqual(404);
|
||||
expect(error.stateText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -134,7 +132,7 @@ describe('PreferenceService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not create preference if error occurred', (done) => {
|
||||
it('Should not create preference if error occurred', () => {
|
||||
getInstanceSpy.and.returnValue(apiErrorMock);
|
||||
service.createPreference('mock-app-name', 'mock-preference-key', createMockPreference)
|
||||
.subscribe(
|
||||
@@ -143,7 +141,6 @@ describe('PreferenceService', () => {
|
||||
expect(error.state).toEqual(404);
|
||||
expect(error.stateText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -160,7 +157,7 @@ describe('PreferenceService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not update preference if error occurred', (done) => {
|
||||
it('Should not update preference if error occurred', () => {
|
||||
getInstanceSpy.and.returnValue(apiErrorMock);
|
||||
service.createPreference('mock-app-name', 'mock-preference-key', updateMockPreference)
|
||||
.subscribe(
|
||||
@@ -169,7 +166,6 @@ describe('PreferenceService', () => {
|
||||
expect(error.state).toEqual(404);
|
||||
expect(error.stateText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -182,7 +178,7 @@ describe('PreferenceService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not delete preference if error occurred', (done) => {
|
||||
it('Should not delete preference if error occurred', () => {
|
||||
getInstanceSpy.and.returnValue(apiErrorMock);
|
||||
service.deletePreference('mock-app-name', 'mock-preference-key')
|
||||
.subscribe(
|
||||
@@ -191,7 +187,6 @@ describe('PreferenceService', () => {
|
||||
expect(error.state).toEqual(404);
|
||||
expect(error.stateText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
+25
-29
@@ -1,33 +1,29 @@
|
||||
|
||||
<mat-nav-list class="adf-task-filters" *ngIf="filters$ | async as filterList; else loading">
|
||||
<button
|
||||
*ngFor="let filter of filterList"
|
||||
mat-list-item
|
||||
(click)="onFilterClick(filter)"
|
||||
[attr.aria-label]="filter.name | translate"
|
||||
[id]="filter.id"
|
||||
[attr.data-automation-id]="filter.key + '_filter'"
|
||||
[class.adf-active]="currentFilter === filter"
|
||||
>
|
||||
<div class="adf-task-filters__entry">
|
||||
<div class="adf-task-filters__entry-label">
|
||||
<adf-icon data-automation-id="adf-filter-icon"
|
||||
*ngIf="showIcons"
|
||||
[value]="filter.icon"
|
||||
></adf-icon>
|
||||
<span data-automation-id="adf-filter-label">
|
||||
{{ filter.name | translate }}
|
||||
</span>
|
||||
</div>
|
||||
<span *ngIf="counters$[filter.key]"
|
||||
[attr.data-automation-id]="filter.key + '_filter-counter'"
|
||||
class="adf-task-filters__entry-counter"
|
||||
[class.adf-active]="wasFilterUpdated(filter.key)">
|
||||
{{ counters$[filter.key] | async }}
|
||||
</span>
|
||||
</div>
|
||||
<ng-container *ngIf="filters$ | async as filterList; else loading">
|
||||
<div *ngFor="let filter of filterList"
|
||||
class="adf-task-filters__entry">
|
||||
<button (click)="onFilterClick(filter)"
|
||||
[attr.aria-label]="filter.name | translate"
|
||||
[id]="filter.id"
|
||||
[attr.data-automation-id]="filter.key + '_filter'"
|
||||
mat-button
|
||||
[class.adf-active]="currentFilter === filter"
|
||||
class="adf-filter-action-button adf-full-width"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign="space-between center">
|
||||
<adf-icon data-automation-id="adf-filter-icon"
|
||||
*ngIf="showIcons"
|
||||
[value]="filter.icon"></adf-icon>
|
||||
<span data-automation-id="adf-filter-label"
|
||||
class="adf-filter-action-button__label">{{ filter.name | translate }}</span>
|
||||
</button>
|
||||
</mat-nav-list>
|
||||
<span *ngIf="counters$[filter.key]"
|
||||
[attr.data-automation-id]="filter.key + '_filter-counter'"
|
||||
class="adf-filter-action-button__counter"
|
||||
[class.adf-active]=wasFilterUpdated(filter.key)>
|
||||
{{ counters$[filter.key] | async }}
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #loading>
|
||||
<ng-container>
|
||||
<div class="adf-app-list-spinner">
|
||||
|
||||
+49
-30
@@ -1,39 +1,58 @@
|
||||
.adf-task-filters {
|
||||
margin-right: calc(-1 * var(--adf-theme-spacing));
|
||||
|
||||
&__entry {
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
color: var(--adf-theme-foreground-text-color-054);
|
||||
.adf {
|
||||
&-task-filters__entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding: 12px 0 !important;
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
font-size: var(--theme-body-1-font-size) !important;
|
||||
font-weight: bold;
|
||||
opacity: 1;
|
||||
|
||||
.adf-full-width {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.adf-filter-action-button {
|
||||
opacity: 0.54;
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.adf-filter-action-button__label {
|
||||
padding-left: 20px;
|
||||
margin: 0 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-filter-action-button__counter {
|
||||
opacity: 0.54;
|
||||
margin-left: 10px;
|
||||
margin-top: 6px;
|
||||
padding: 0 5px;
|
||||
|
||||
&.adf-active {
|
||||
margin-left: 8px;
|
||||
margin-top: 5px;
|
||||
padding: 0 5px;
|
||||
border-radius: 15px;
|
||||
background-color: var(--theme-accent-color);
|
||||
color: var(--adf-theme-mat-grey-color-50);
|
||||
font-size: smaller;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-primary-color);
|
||||
|
||||
.adf-filter-action-button__counter,
|
||||
.adf-filter-action-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__entry-label {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: var(--adf-theme-spacing);
|
||||
}
|
||||
|
||||
&__entry-counter {
|
||||
padding: 0 5px;
|
||||
border-radius: 15px;
|
||||
|
||||
&.adf-active {
|
||||
background-color: var(--theme-accent-color);
|
||||
color: var(--adf-theme-mat-grey-color-50);
|
||||
font-size: smaller;
|
||||
.adf-active {
|
||||
color: var(--theme-primary-color);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-active .adf-task-filters__entry-label {
|
||||
color: var(--theme-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -31,7 +31,6 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
|
||||
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { Environment } from '../../../../common/interface/environment.interface';
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
@@ -68,10 +67,6 @@ export abstract class BaseEditTaskFilterCloudComponent<T> implements OnInit, OnC
|
||||
@Input()
|
||||
id: string;
|
||||
|
||||
/** List of environments. */
|
||||
@Input()
|
||||
environmentList: Environment[] = [];
|
||||
|
||||
/** processInstanceId of the task filter. */
|
||||
@Input()
|
||||
processInstanceId: string;
|
||||
@@ -104,6 +99,14 @@ export abstract class BaseEditTaskFilterCloudComponent<T> implements OnInit, OnC
|
||||
@Input()
|
||||
sortProperties: string[] = [];
|
||||
|
||||
/** Task Filter to use*/
|
||||
@Input()
|
||||
taskFilter: T;
|
||||
|
||||
/** Emitted when a task filter property changes. */
|
||||
@Output()
|
||||
filterChange = new EventEmitter<T>();
|
||||
|
||||
/** Emitted when a filter action occurs (i.e Save, Save As, Delete). */
|
||||
@Output()
|
||||
action = new EventEmitter<TaskFilterAction>();
|
||||
@@ -126,15 +129,8 @@ export abstract class BaseEditTaskFilterCloudComponent<T> implements OnInit, OnC
|
||||
label: 'ADF_CLOUD_TASK_FILTERS.STATUS.ALL'
|
||||
};
|
||||
|
||||
@Input()
|
||||
taskFilter: T;
|
||||
|
||||
changedTaskFilter: T;
|
||||
|
||||
/** Emitted when a task filter property changes. */
|
||||
@Output()
|
||||
filterChange = new EventEmitter<T>();
|
||||
|
||||
protected onDestroy$ = new Subject<boolean>();
|
||||
isLoading: boolean = false;
|
||||
|
||||
@@ -248,7 +244,7 @@ export abstract class BaseEditTaskFilterCloudComponent<T> implements OnInit, OnC
|
||||
.subscribe((applications) => {
|
||||
if (applications && applications.length > 0) {
|
||||
applications.map((application) => {
|
||||
this.applicationNames.push({ label: this.appsProcessCloudService.getApplicationLabel(application, this.environmentList), value: application.name });
|
||||
this.applicationNames.push({ label: application.name, value: application.name });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+71
-87
@@ -15,18 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { of, Subject } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||
import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
|
||||
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from '../../../../app/mock/app-model.mock';
|
||||
import { fakeApplicationInstance } from '../../../../app/mock/app-model.mock';
|
||||
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
|
||||
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
|
||||
import { TaskCloudService } from '../../../services/task-cloud.service';
|
||||
@@ -35,9 +35,6 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { EditServiceTaskFilterCloudComponent } from './edit-service-task-filter-cloud.component';
|
||||
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
|
||||
import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
|
||||
import { fakeEnvironmentList } from '../../../../common/mock/environment.mock';
|
||||
import { mockApplicationTaskFilterProperties } from '../../mock/edit-task-filter-cloud.mock';
|
||||
|
||||
describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
let component: EditServiceTaskFilterCloudComponent;
|
||||
@@ -48,7 +45,6 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
let getTaskFilterSpy: jasmine.Spy;
|
||||
let getRunningApplicationsSpy: jasmine.Spy;
|
||||
let taskService: TaskCloudService;
|
||||
const afterClosedSubject = new Subject<any>();
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@@ -70,10 +66,13 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
appsService = TestBed.inject(AppsProcessCloudService);
|
||||
taskService = TestBed.inject(TaskCloudService);
|
||||
dialog = TestBed.inject(MatDialog);
|
||||
const dialogRefMock: any = {
|
||||
afterClosed: () => afterClosedSubject
|
||||
};
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
spyOn(dialog, 'open').and.returnValue({
|
||||
afterClosed: of({
|
||||
action: EditServiceTaskFilterCloudComponent.ACTION_SAVE,
|
||||
icon: 'icon',
|
||||
name: 'fake-name'
|
||||
})
|
||||
} as any);
|
||||
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeServiceFilter));
|
||||
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
|
||||
fixture.detectChanges();
|
||||
@@ -81,17 +80,18 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
|
||||
afterEach(() => fixture.destroy());
|
||||
|
||||
it('should fetch task filter by taskId', async () => {
|
||||
it('should fetch task filter by taskId', () => {
|
||||
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
|
||||
component.ngOnChanges({ id: taskFilterIdChange });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(getTaskFilterSpy).toHaveBeenCalled();
|
||||
expect(component.taskFilter.name).toEqual('FakeInvolvedTasks');
|
||||
expect(component.taskFilter.icon).toEqual('adjust');
|
||||
expect(component.taskFilter.status).toEqual('COMPLETED');
|
||||
expect(component.taskFilter.order).toEqual('ASC');
|
||||
expect(component.taskFilter.sort).toEqual('id');
|
||||
fixture.whenStable().then(() => {
|
||||
expect(getTaskFilterSpy).toHaveBeenCalled();
|
||||
expect(component.taskFilter.name).toEqual('FakeInvolvedTasks');
|
||||
expect(component.taskFilter.icon).toEqual('adjust');
|
||||
expect(component.taskFilter.status).toEqual('CREATED');
|
||||
expect(component.taskFilter.order).toEqual('ASC');
|
||||
expect(component.taskFilter.sort).toEqual('id');
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch process definitions when processDefinitionName filter property is set', async () => {
|
||||
@@ -636,7 +636,7 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
spyOn(component.action, 'emit').and.callThrough();
|
||||
});
|
||||
|
||||
it('should emit save event and save the filter on click save button', async () => {
|
||||
it('should emit save event and save the filter on click save button', fakeAsync(() => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'updateFilter').and.returnValue(of(null));
|
||||
fixture.detectChanges();
|
||||
@@ -650,15 +650,16 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
sortOptions[3].nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(saveButton.disabled).toBe(false);
|
||||
saveButton.click();
|
||||
expect(service.updateFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(saveButton.disabled).toBe(false);
|
||||
saveButton.click();
|
||||
expect(service.updateFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit delete event and delete the filter on click of delete button', async () => {
|
||||
it('should emit delete event and delete the filter on click of delete button', fakeAsync(() => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'deleteFilter').and.returnValue(of(null));
|
||||
fixture.detectChanges();
|
||||
@@ -669,48 +670,40 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
stateElement.click();
|
||||
fixture.detectChanges();
|
||||
const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit saveAs event and add filter on click saveAs button', async () => {
|
||||
it('should emit saveAs event and add filter on click saveAs button', fakeAsync(() => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'addFilter').and.returnValue(of(null));
|
||||
fixture.detectChanges();
|
||||
|
||||
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
|
||||
expansionPanel.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
|
||||
stateElement.click();
|
||||
const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
|
||||
sortElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const sortOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-sort"] .mat-option-ripple'));
|
||||
sortOptions[3].nativeElement.click();
|
||||
const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
|
||||
sortOptions[2].nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
|
||||
saveButton.dispatchEvent(new Event('click'));
|
||||
fixture.detectChanges();
|
||||
afterClosedSubject.next({
|
||||
action: TaskFilterDialogCloudComponent.ACTION_SAVE,
|
||||
icon: 'icon',
|
||||
name: 'fake-name'
|
||||
const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(saveAsButton.disabled).toBe(false);
|
||||
saveAsButton.click();
|
||||
expect(service.addFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(dialog.open).toHaveBeenCalled();
|
||||
});
|
||||
await fixture.whenStable();
|
||||
}));
|
||||
|
||||
expect(service.addFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(dialog.open).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call restore default filters service on deletion of last filter', async () => {
|
||||
it('should call restore default filters service on deletion of last filter', fakeAsync(() => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'deleteFilter').and.returnValue(of([]));
|
||||
const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
|
||||
@@ -722,16 +715,17 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
stateElement.click();
|
||||
fixture.detectChanges();
|
||||
const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(restoreDefaultFiltersSpy).toHaveBeenCalled();
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(restoreDefaultFiltersSpy).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not call restore default filters service on deletion of first filter', async () => {
|
||||
it('should not call restore default filters service on deletion of first filter', fakeAsync(() => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'deleteFilter').and.returnValue(of([{ name: 'mock-filter-name' }]));
|
||||
const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
|
||||
@@ -743,24 +737,14 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
stateElement.click();
|
||||
fixture.detectChanges();
|
||||
const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(restoreDefaultFiltersSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should add environment name to each application selector option label', () => {
|
||||
component.appName = fakeApplicationInstance[0].name;
|
||||
component.environmentList = fakeEnvironmentList;
|
||||
|
||||
getRunningApplicationsSpy.and.returnValue(of(fakeApplicationInstanceWithEnvironment));
|
||||
spyOn(component, 'createTaskFilterProperties').and.returnValue(mockApplicationTaskFilterProperties);
|
||||
|
||||
const filteredProperties = component.createAndFilterProperties();
|
||||
expect(filteredProperties[0].options[0].label).toBe('application-new-1 (test-env-name-1)');
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(restoreDefaultFiltersSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
+50
-58
@@ -15,12 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { of, Subject } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
|
||||
@@ -54,8 +54,6 @@ import {
|
||||
} from '../../mock/edit-task-filter-cloud.mock';
|
||||
import { mockFoodUsers } from '../../../../people/mock/people-cloud.mock';
|
||||
import { mockFoodGroups } from '../../../../group/mock/group-cloud.mock';
|
||||
import { SimpleChanges } from '@angular/core';
|
||||
import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
|
||||
|
||||
describe('EditTaskFilterCloudComponent', () => {
|
||||
let component: EditTaskFilterCloudComponent;
|
||||
@@ -67,7 +65,6 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
let getTaskFilterSpy: jasmine.Spy;
|
||||
let getRunningApplicationsSpy: jasmine.Spy;
|
||||
let taskService: TaskCloudService;
|
||||
const afterClosedSubject = new Subject<any>();
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@@ -91,10 +88,13 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
taskService = TestBed.inject(TaskCloudService);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
dialog = TestBed.inject(MatDialog);
|
||||
const dialogRefMock: any = {
|
||||
afterClosed: () => afterClosedSubject
|
||||
};
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
spyOn(dialog, 'open').and.returnValue({
|
||||
afterClosed: of({
|
||||
action: EditTaskFilterCloudComponent.ACTION_SAVE,
|
||||
icon: 'icon',
|
||||
name: 'fake-name'
|
||||
})
|
||||
} as any);
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mockAlfrescoApi);
|
||||
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));
|
||||
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
|
||||
@@ -103,17 +103,17 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
|
||||
afterEach(() => fixture.destroy());
|
||||
|
||||
it('should fetch task filter by taskId', async () => {
|
||||
it('should fetch task filter by taskId', () => {
|
||||
component.ngOnChanges({ id: mockTaskFilterIdChange });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(getTaskFilterSpy).toHaveBeenCalled();
|
||||
expect(component.taskFilter.name).toEqual('FakeInvolvedTasks');
|
||||
expect(component.taskFilter.icon).toEqual('adjust');
|
||||
expect(component.taskFilter.status).toEqual(TaskStatusFilter.CREATED);
|
||||
expect(component.taskFilter.order).toEqual('ASC');
|
||||
expect(component.taskFilter.sort).toEqual('id');
|
||||
fixture.whenStable().then(() => {
|
||||
expect(getTaskFilterSpy).toHaveBeenCalled();
|
||||
expect(component.taskFilter.name).toEqual('FakeInvolvedTasks');
|
||||
expect(component.taskFilter.icon).toEqual('adjust');
|
||||
expect(component.taskFilter.status).toEqual(TaskStatusFilter.CREATED);
|
||||
expect(component.taskFilter.order).toEqual('ASC');
|
||||
expect(component.taskFilter.sort).toEqual('id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('processInstanceId filter', () => {
|
||||
@@ -192,6 +192,7 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
expect(getProcessInstanceIdInputElement().value).toEqual('fakeProcessInstanceIdFromResponse');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should fetch process definitions when processDefinitionName filter property is set', async () => {
|
||||
@@ -955,39 +956,36 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
describe('edit filter actions', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.changedTaskFilter = { name: 'mock-filter-name' } as TaskFilterCloudModel;
|
||||
component.ngOnChanges({ id: mockTaskFilterIdChange } as SimpleChanges);
|
||||
spyOn(component.action, 'emit').and.stub();
|
||||
component.toggleFilterActions = true;
|
||||
component.ngOnChanges({ id: mockTaskFilterIdChange });
|
||||
fixture.detectChanges();
|
||||
spyOn(component.action, 'emit').and.callThrough();
|
||||
});
|
||||
|
||||
it('should emit save event and save the filter on click save button', fakeAsync(() => {
|
||||
spyOn(service, 'updateFilter').and.returnValue(of([new TaskFilterCloudModel({ name: 'mock-filter-name' })]));
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'updateFilter').and.returnValue(of(null));
|
||||
fixture.detectChanges();
|
||||
|
||||
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
|
||||
expansionPanel.click();
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
|
||||
stateElement.click();
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
const sortOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-sort"] .mat-option-ripple'));
|
||||
const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
|
||||
sortOptions[3].nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
tick(550);
|
||||
|
||||
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
|
||||
saveButton.dispatchEvent(new Event('click'));
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(service.updateFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
fixture.whenStable().then(() => {
|
||||
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
|
||||
expect(saveButton.disabled).toBe(false);
|
||||
saveButton.click();
|
||||
expect(service.updateFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit delete event and delete the filter on click of delete button', async () => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'deleteFilter').and.returnValue(of(null));
|
||||
fixture.detectChanges();
|
||||
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
|
||||
@@ -1000,45 +998,38 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
|
||||
expect(deleteButton.getAttribute('disabled')).toBeNull();
|
||||
expect(deleteButton.disabled).toBe(false);
|
||||
deleteButton.click();
|
||||
expect(service.deleteFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit saveAs event and add filter on click saveAs button', async () => {
|
||||
it('should emit saveAs event and add filter on click saveAs button', fakeAsync(() => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'addFilter').and.returnValue(of(null));
|
||||
fixture.detectChanges();
|
||||
|
||||
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
|
||||
expansionPanel.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
|
||||
stateElement.click();
|
||||
const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
|
||||
sortElement.click();
|
||||
fixture.detectChanges();
|
||||
const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
|
||||
sortOptions[2].nativeElement.click();
|
||||
|
||||
const sortOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-sort"] .mat-option-ripple'));
|
||||
sortOptions[3].nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
|
||||
saveButton.dispatchEvent(new Event('click'));
|
||||
fixture.detectChanges();
|
||||
afterClosedSubject.next({
|
||||
action: TaskFilterDialogCloudComponent.ACTION_SAVE,
|
||||
icon: 'icon',
|
||||
name: 'fake-name'
|
||||
fixture.whenStable().then(() => {
|
||||
const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
|
||||
expect(saveAsButton.disabled).toBe(false);
|
||||
saveAsButton.click();
|
||||
expect(service.addFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(dialog.open).toHaveBeenCalled();
|
||||
});
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(service.addFilter).toHaveBeenCalled();
|
||||
expect(component.action.emit).toHaveBeenCalled();
|
||||
expect(dialog.open).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call restore default filters service on deletion of last filter', async () => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'deleteFilter').and.returnValue(of([]));
|
||||
const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
@@ -1060,6 +1051,7 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
});
|
||||
|
||||
it('should not call restore default filters service on deletion of first filter', async () => {
|
||||
component.toggleFilterActions = true;
|
||||
spyOn(service, 'deleteFilter').and.returnValue(of([new TaskFilterCloudModel({ name: 'mock-filter-name' })]));
|
||||
const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
|
||||
}
|
||||
}
|
||||
|
||||
protected updateFilter(filterToUpdate: TaskFilterCloudModel): Observable<TaskFilterCloudModel[]> {
|
||||
protected updateFilter(filterToUpdate: TaskFilterCloudModel): Observable<any> {
|
||||
return this.taskFilterCloudService.updateFilter(filterToUpdate);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -276,7 +276,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const filterCounters = fixture.debugElement.queryAll(By.css('.adf-task-filters__entry-counter'));
|
||||
const filterCounters = fixture.debugElement.queryAll(By.css('.adf-filter-action-button__counter'));
|
||||
expect(component.filters.length).toBe(3);
|
||||
expect(filterCounters.length).toBe(1);
|
||||
expect(filterCounters[0].nativeElement.innerText).toContain('11');
|
||||
|
||||
-25
@@ -108,28 +108,3 @@ export const mockCreatedDateFilter = {
|
||||
to: '_createdTo'
|
||||
}
|
||||
};
|
||||
|
||||
export const mockApplicationsSelectorOptions = [
|
||||
{
|
||||
label: 'application-new-1 (test-env-name-1)',
|
||||
value: 'application-new-1'
|
||||
},
|
||||
{
|
||||
label: 'application-new-1 (test-env-name-1)',
|
||||
value: 'application-new-1'
|
||||
},
|
||||
{
|
||||
label: 'application-new-1 (test-env-name-1)',
|
||||
value: 'application-new-1'
|
||||
}
|
||||
];
|
||||
|
||||
export const mockApplicationTaskFilterProperties = [
|
||||
{
|
||||
label: 'test-label',
|
||||
type: 'select',
|
||||
key: 'appName',
|
||||
value: mockApplicationsSelectorOptions[0].value,
|
||||
options: mockApplicationsSelectorOptions
|
||||
}
|
||||
];
|
||||
|
||||
@@ -29,7 +29,6 @@ export class TaskFilterCloudModel {
|
||||
id: string;
|
||||
name: string;
|
||||
key: string;
|
||||
environmentId?: string;
|
||||
icon: string;
|
||||
index: number;
|
||||
appName: string;
|
||||
@@ -73,7 +72,6 @@ export class TaskFilterCloudModel {
|
||||
this.id = obj.id || Math.random().toString(36).substr(2, 9);
|
||||
this.name = obj.name || null;
|
||||
this.key = obj.key || null;
|
||||
this.environmentId = obj.environmentId || null;
|
||||
this.icon = obj.icon || null;
|
||||
this.index = obj.index || null;
|
||||
this.appName = obj.appName || obj.appName === '' ? obj.appName : null;
|
||||
@@ -193,7 +191,6 @@ export class TaskFilterCloudModel {
|
||||
export interface ServiceTaskFilterCloudModel {
|
||||
id?: string;
|
||||
name?: string;
|
||||
environmentId?: string;
|
||||
key?: string;
|
||||
icon?: string;
|
||||
index?: number;
|
||||
|
||||
+21
-30
@@ -85,10 +85,6 @@ describe('TaskFormCloudComponent', () => {
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('Complete button', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -289,62 +285,57 @@ describe('TaskFormCloudComponent', () => {
|
||||
beforeEach(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should emit cancelClick when cancel button is clicked', async () => {
|
||||
spyOn(component.cancelClick,'emit').and.stub();
|
||||
it('should emit cancelClick when cancel button is clicked', (done) => {
|
||||
component.cancelClick.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
|
||||
cancelBtn.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component.cancelClick.emit).toHaveBeenCalledOnceWith('task1');
|
||||
cancelBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskCompleted when task is completed', async () => {
|
||||
it('should emit taskCompleted when task is completed', (done) => {
|
||||
spyOn(taskCloudService, 'completeTask').and.returnValue(of({}));
|
||||
spyOn(component.taskCompleted, 'emit').and.stub();
|
||||
|
||||
component.taskCompleted.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
|
||||
completeBtn.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component.taskCompleted.emit).toHaveBeenCalledOnceWith('task1');
|
||||
completeBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskClaimed when task is claimed', async () => {
|
||||
it('should emit taskClaimed when task is claimed', (done) => {
|
||||
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
|
||||
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
|
||||
spyOn(component.taskClaimed, 'emit').and.stub();
|
||||
taskDetails.status = TASK_CREATED_STATE;
|
||||
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
|
||||
getTaskSpy.and.returnValue(of(taskDetails));
|
||||
|
||||
component.taskClaimed.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
|
||||
fixture.detectChanges();
|
||||
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
|
||||
claimBtn.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component.taskClaimed.emit).toHaveBeenCalledOnceWith('task1');
|
||||
claimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit error when error occurs', async () => {
|
||||
spyOn(component.error, 'emit').and.stub();
|
||||
it('should emit error when error occurs', (done) => {
|
||||
component.error.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.onError({});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component.error.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reload when task is completed', () => {
|
||||
|
||||
-1
@@ -81,7 +81,6 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent {
|
||||
skipCount: this.skipCount,
|
||||
sorting: this.sorting,
|
||||
id: this.queryParams?.serviceTaskId,
|
||||
environmentId: this.queryParams?.environmentId,
|
||||
activityName: this.queryParams?.activityName,
|
||||
activityType: this.queryParams?.activityType,
|
||||
completedDate: this.queryParams?.completedDate,
|
||||
|
||||
+39
-49
@@ -16,20 +16,9 @@
|
||||
*/
|
||||
|
||||
import { Component, SimpleChange, ViewChild } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { AppConfigService,
|
||||
setupTestBed,
|
||||
DataRowEvent,
|
||||
ObjectDataRow,
|
||||
User,
|
||||
DataColumn,
|
||||
ColumnsSelectorComponent,
|
||||
AlfrescoApiService,
|
||||
AlfrescoApiServiceMock,
|
||||
AppConfigServiceMock,
|
||||
TranslationService,
|
||||
TranslationMock } from '@alfresco/adf-core';
|
||||
import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow, User, DataColumn, ColumnsSelectorComponent } 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';
|
||||
@@ -40,9 +29,6 @@ import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.mod
|
||||
import { shareReplay, skip } from 'rxjs/operators';
|
||||
import { TaskListCloudServiceInterface } from '../../../services/task-list-cloud.service.interface';
|
||||
import { TASK_LIST_CLOUD_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { TaskListCloudModule } from '../task-list-cloud.module';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -102,6 +88,9 @@ describe('TaskListCloudComponent', () => {
|
||||
TranslateModule.forRoot(),
|
||||
ProcessServiceCloudTestingModule
|
||||
],
|
||||
declarations: [
|
||||
EmptyTemplateComponent
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: TASK_LIST_CLOUD_TOKEN,
|
||||
@@ -534,20 +523,6 @@ describe('TaskListCloudComponent', () => {
|
||||
describe('Creating an empty custom template - EmptyTemplateComponent', () => {
|
||||
let fixtureEmpty: ComponentFixture<EmptyTemplateComponent>;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
NoopAnimationsModule,
|
||||
TranslateModule.forRoot(),
|
||||
TaskListCloudModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
||||
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
||||
{ provide: TranslationService, useClass: TranslationMock }
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const emptyList = { list: { entries: [] } };
|
||||
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
|
||||
@@ -560,12 +535,16 @@ describe('TaskListCloudComponent', () => {
|
||||
fixtureEmpty.destroy();
|
||||
});
|
||||
|
||||
it('should render the custom template', async () => {
|
||||
// TODO still not working because of the Loading Spinner
|
||||
// eslint-disable-next-line
|
||||
xit('should render the custom template', (done) => {
|
||||
fixtureEmpty.detectChanges();
|
||||
await fixtureEmpty.whenStable();
|
||||
fixtureEmpty.detectChanges();
|
||||
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
|
||||
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
|
||||
fixtureEmpty.whenStable().then(() => {
|
||||
fixtureEmpty.detectChanges();
|
||||
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
|
||||
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -602,7 +581,7 @@ describe('TaskListCloudComponent', () => {
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'priority',
|
||||
key: 'entry.priority',
|
||||
type: 'text',
|
||||
title: 'ADF_TASK_LIST.PROPERTIES.PRIORITY',
|
||||
sortable: true
|
||||
@@ -623,34 +602,45 @@ describe('TaskListCloudComponent', () => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should show tooltip if config copyContent flag is true', fakeAsync(() => {
|
||||
// TODO: highly unstable test
|
||||
// eslint-disable-next-line
|
||||
xit('should show tooltip if config copyContent flag is true', fakeAsync(() => {
|
||||
taskSpy.and.returnValue(of(fakeGlobalTasks));
|
||||
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||
|
||||
component.success.subscribe(() => {
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]');
|
||||
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
component.presetColumn = 'fakeCustomSchema';
|
||||
component.appName = appName.currentValue;
|
||||
component.ngOnChanges({ appName });
|
||||
component.ngAfterContentInit();
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]');
|
||||
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
|
||||
}));
|
||||
|
||||
it('should replace priority values', fakeAsync(() => {
|
||||
// TODO: highly unstable test
|
||||
// eslint-disable-next-line
|
||||
xit('should replace priority values', (done) => {
|
||||
taskSpy.and.returnValue(of(fakeGlobalTasks));
|
||||
component.presetColumn = 'fakeCustomSchema';
|
||||
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||
component.ngOnChanges({ appName });
|
||||
|
||||
component.success.subscribe(() => {
|
||||
const cell = fixture.nativeElement.querySelector('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]');
|
||||
expect(cell.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE');
|
||||
done();
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
component.reload();
|
||||
tick();
|
||||
const cell = fixture.nativeElement.querySelector('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]');
|
||||
expect(cell.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE');
|
||||
}));
|
||||
});
|
||||
|
||||
it('replacePriorityValues should return undefined when no rows defined', () => {
|
||||
const emptyList = { list: { entries: [] } };
|
||||
|
||||
-5
@@ -89,10 +89,6 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessLi
|
||||
@Input()
|
||||
name: string = '';
|
||||
|
||||
/** Filter the tasks to display only the ones with this environment ID. */
|
||||
@Input()
|
||||
environmentId: string;
|
||||
|
||||
/** Filter the tasks. Display only tasks with parentTaskId equal to the supplied value. */
|
||||
@Input()
|
||||
parentTaskId: string = '';
|
||||
@@ -194,7 +190,6 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessLi
|
||||
assignee: this.assignee,
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
environmentId: this.environmentId,
|
||||
parentTaskId: this.parentTaskId,
|
||||
processDefinitionName: this.processDefinitionName,
|
||||
processDefinitionId: this.processDefinitionId,
|
||||
|
||||
@@ -20,7 +20,6 @@ import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.mod
|
||||
export interface ServiceTaskQueryCloudRequestModel {
|
||||
appName: string;
|
||||
appVersion?: string;
|
||||
environmentId?: string;
|
||||
id?: string;
|
||||
status?: string;
|
||||
maxItems: number;
|
||||
|
||||
+4
-8
@@ -132,43 +132,39 @@ describe('Activiti ServiceTaskList Cloud Service', () => {
|
||||
|
||||
});
|
||||
|
||||
it('should throw an exeption and execute logService error if appName is null', (done) => {
|
||||
it('should throw an exeption and execute logService error if appName is null', () => {
|
||||
const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured';
|
||||
const params = [null, 'executionId_1', 'flowNodeId_1'] as const;
|
||||
service.replayServiceTaskRequest(...params).toPromise().catch((error) => {
|
||||
expect(error).toEqual(expectedErrorMessage);
|
||||
expect(logServiceErrorSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an exeption and execute logService error if executionId is null', (done) => {
|
||||
it('should throw an exeption and execute logService error if executionId is null', () => {
|
||||
const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured';
|
||||
const params = ['fakeName', null, 'flowNodeId_1'] as const;
|
||||
service.replayServiceTaskRequest(...params).toPromise().catch((error) => {
|
||||
expect(error).toEqual(expectedErrorMessage);
|
||||
expect(logServiceErrorSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an exeption and execute logService error if flowNodeId is null', (done) => {
|
||||
it('should throw an exeption and execute logService error if flowNodeId is null', () => {
|
||||
const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured';
|
||||
const params = ['fakeName', 'executionId_1', null] as const;
|
||||
service.replayServiceTaskRequest(...params).toPromise().catch((error) => {
|
||||
expect(error).toEqual(expectedErrorMessage);
|
||||
expect(logServiceErrorSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an exeption and execute logService error if appName, executionId and flowNodeId are null', (done) => {
|
||||
it('should throw an exeption and execute logService error if appName, executionId and flowNodeId are null', () => {
|
||||
const expectedErrorMessage = 'Appname/executionId/flowNodeId not configured';
|
||||
const params = [null, null, null] as const;
|
||||
service.replayServiceTaskRequest(...params).toPromise().catch((error) => {
|
||||
expect(error).toEqual(expectedErrorMessage);
|
||||
expect(logServiceErrorSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,7 +26,6 @@ export * from './lib/services/public-api';
|
||||
export * from './lib/rich-text-editor/public-api';
|
||||
|
||||
export * from './lib/types';
|
||||
export * from './lib/common/index';
|
||||
export * from './lib/pipes/process-name-cloud.pipe';
|
||||
export * from './lib/pipes/process-services-cloud-pipe.module';
|
||||
export * from './lib/models/process-definition-cloud.model';
|
||||
|
||||
@@ -20,7 +20,7 @@ import { BrowserActions } from '../utils/browser-actions';
|
||||
|
||||
export class BreadcrumbPage {
|
||||
|
||||
breadcrumb = $(`adf-breadcrumb nav[data-automation-id='breadcrumb_break_it']`);
|
||||
breadcrumb = $(`adf-breadcrumb nav[data-automation-id='breadcrumb']`);
|
||||
currentItem = $('.adf-breadcrumb-item-current');
|
||||
|
||||
async chooseBreadCrumb(breadCrumbItem: string): Promise<void> {
|
||||
|
||||
@@ -26,19 +26,19 @@ export interface LoginOptions {
|
||||
}
|
||||
export class LoginPage {
|
||||
|
||||
loginUrl = `${browser.baseUrl}/login/break-all-e2es`;
|
||||
loginUrl = `${browser.baseUrl}/login`;
|
||||
|
||||
ssoButton = $(`[data-automation-id="login-button-sso-break-it"]`);
|
||||
usernameField = $('#username_break_it');
|
||||
passwordField = $('#password_break_it');
|
||||
loginButton = $('input[type="submit_break_it"]');
|
||||
userIcon = $(`[data-automation-id*='user-initials_break_it']`);
|
||||
loginError = $(`div[data-automation-id="login-error_break_it"]`);
|
||||
ssoButton = $(`[data-automation-id="login-button-sso"]`);
|
||||
usernameField = $('#username');
|
||||
passwordField = $('#password');
|
||||
loginButton = $('input[type="submit"]');
|
||||
userIcon = $(`[data-automation-id*='user-initials']`);
|
||||
loginError = $(`div[data-automation-id="login-error"]`);
|
||||
visibilityLabel = $('#v');
|
||||
|
||||
txtUsernameBasicAuth = $('input[id="username_break_it"]');
|
||||
txtPasswordBasicAuth = $('input[id="password_break_it"]');
|
||||
signInButtonBasicAuth = $('#login-button_break_it');
|
||||
txtUsernameBasicAuth = $('input[id="username"]');
|
||||
txtPasswordBasicAuth = $('input[id="password"]');
|
||||
signInButtonBasicAuth = $('#login-button');
|
||||
|
||||
async goToLoginPage(): Promise<void> {
|
||||
let currentUrl;
|
||||
|
||||
@@ -22,27 +22,27 @@ import { Logger } from '../utils/logger';
|
||||
|
||||
export class PaginationPage {
|
||||
|
||||
pageSelectorDropDown = $('div[class*="adf-pagination__page-selector_break_it"]');
|
||||
pageSelectorArrow = $('button[data-automation-id="page-selector_break_it"]');
|
||||
itemsPerPage = $('.adf-pagination__max-items_break_it');
|
||||
itemsPerPageOpenDropdown = $('.adf-pagination__perpage-block button_break_it');
|
||||
itemsPerPageOptions = $$('.adf-pagination__page-selector .mat-menu-item_break_it');
|
||||
currentPage = $('.adf-pagination__current-page_break_it');
|
||||
totalPages = $('.adf-pagination__total-pages_break_it');
|
||||
paginationRange = $('.adf-pagination__range_break_it');
|
||||
nextPageButton = $('button[class*="adf-pagination__next-button_break_it"]');
|
||||
nextButtonDisabled = $('button[class*="adf-pagination__next-button_break_it"][disabled]');
|
||||
previousButtonDisabled = $('button[class*="adf-pagination__previous-button_break_it"][disabled]');
|
||||
pageDropDown = $('div[class*="adf-pagination__actualinfo-block_break_it"] button');
|
||||
pageDropDownOptions = $$('div[class*="mat-menu-content_break_it"] button');
|
||||
paginationSection = $('adf-pagination_break_it');
|
||||
paginationSectionEmpty = $('adf-pagination[class*="adf-pagination__empty_break_it"]');
|
||||
totalFiles = $('.adf-pagination__range_break_it');
|
||||
pageSelectorDropDown = $('div[class*="adf-pagination__page-selector"]');
|
||||
pageSelectorArrow = $('button[data-automation-id="page-selector"]');
|
||||
itemsPerPage = $('.adf-pagination__max-items');
|
||||
itemsPerPageOpenDropdown = $('.adf-pagination__perpage-block button');
|
||||
itemsPerPageOptions = $$('.adf-pagination__page-selector .mat-menu-item');
|
||||
currentPage = $('.adf-pagination__current-page');
|
||||
totalPages = $('.adf-pagination__total-pages');
|
||||
paginationRange = $('.adf-pagination__range');
|
||||
nextPageButton = $('button[class*="adf-pagination__next-button"]');
|
||||
nextButtonDisabled = $('button[class*="adf-pagination__next-button"][disabled]');
|
||||
previousButtonDisabled = $('button[class*="adf-pagination__previous-button"][disabled]');
|
||||
pageDropDown = $('div[class*="adf-pagination__actualinfo-block"] button');
|
||||
pageDropDownOptions = $$('div[class*="mat-menu-content"] button');
|
||||
paginationSection = $('adf-pagination');
|
||||
paginationSectionEmpty = $('adf-pagination[class*="adf-pagination__empty"]');
|
||||
totalFiles = $('.adf-pagination__range');
|
||||
|
||||
async selectItemsPerPage(numberOfItem: string): Promise<void> {
|
||||
await BrowserActions.clickExecuteScript(`div[class*="adf-pagination__perpage-block_break_it"] button`);
|
||||
await BrowserActions.clickExecuteScript(`div[class*="adf-pagination__perpage-block"] button`);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.pageSelectorDropDown);
|
||||
const itemsPerPage = element.all(by.cssContainingText('.mat-menu-item_break_it', numberOfItem)).first();
|
||||
const itemsPerPage = element.all(by.cssContainingText('.mat-menu-item', numberOfItem)).first();
|
||||
await BrowserVisibility.waitUntilElementIsPresent(itemsPerPage);
|
||||
await BrowserActions.click(itemsPerPage);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(this.pageSelectorDropDown);
|
||||
|
||||
Generated
+529
-637
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -78,7 +78,7 @@
|
||||
"@storybook/core-server": "6.4.22",
|
||||
"angular-oauth2-oidc": "^13.0.1",
|
||||
"angular-oauth2-oidc-jwks": "^15.0.1",
|
||||
"apollo-angular": "^4.2.1",
|
||||
"apollo-angular": "^4.0.1",
|
||||
"chart.js": "2.9.4",
|
||||
"cropperjs": "1.5.13",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
@@ -98,7 +98,7 @@
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "14.1.3",
|
||||
"@angular-devkit/schematics": "~14.1.0",
|
||||
"@angular-eslint/eslint-plugin": "15.2.1",
|
||||
"@angular-eslint/eslint-plugin": "14.0.4",
|
||||
"@angular-eslint/eslint-plugin-template": "14.3.0",
|
||||
"@angular-eslint/template-parser": "14.0.2",
|
||||
"@angular/cli": "~14.1.0",
|
||||
@@ -126,7 +126,7 @@
|
||||
"@types/pdfjs-dist": "^2.10.378",
|
||||
"@types/request": "^2.48.5",
|
||||
"@types/selenium-webdriver": "^4.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "5.57.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.47.0",
|
||||
"@typescript-eslint/parser": "5.38.1",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"commander": "6.2.1",
|
||||
@@ -140,7 +140,7 @@
|
||||
"eslint-plugin-jsdoc": "40.1.0",
|
||||
"eslint-plugin-license-header": "0.6.0",
|
||||
"eslint-plugin-prefer-arrow": "1.2.3",
|
||||
"eslint-plugin-rxjs": "^5.0.3",
|
||||
"eslint-plugin-rxjs": "^5.0.2",
|
||||
"eslint-plugin-unicorn": "^46.0.0",
|
||||
"github-api": "^3.4.0",
|
||||
"graphql": "^16.6.0",
|
||||
@@ -172,7 +172,7 @@
|
||||
"protractor-retry-angular-cli": "^2.0.3",
|
||||
"protractor-screenshoter-plugin": "0.10.3",
|
||||
"protractor-smartrunner": "^2.0.0-beta6",
|
||||
"rimraf": "^4.4.1",
|
||||
"rimraf": "^4.4.0",
|
||||
"sass-loader": "10.1.1",
|
||||
"selenium-webdriver": "4.1.0",
|
||||
"stylelint": "^14.15.0",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
set -e
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
verifyLib=$1;
|
||||
deps=$2;
|
||||
cd $DIR/../../
|
||||
|
||||
if [ "${GITHUB_EVENT_NAME}" == "schedule" ]; then
|
||||
@@ -10,27 +9,13 @@ if [ "${GITHUB_EVENT_NAME}" == "schedule" ]; then
|
||||
echo true
|
||||
exit 0
|
||||
fi
|
||||
isAffected=false
|
||||
AFFECTED_LIBS=$(npx nx print-affected --type=lib --select=projects ${NX_CALCULATION_FLAGS} --plain)
|
||||
#echo "Verify if affected build contains $1"
|
||||
#echo "Affected libs:$AFFECTED_LIBS"
|
||||
if [[ $AFFECTED_LIBS =~ $verifyLib ]]; then
|
||||
#echo "Yep project:$verifyLib is affected carry on"
|
||||
isAffected=true
|
||||
#echo "Yep project:$verifyLib is affected carry on"
|
||||
echo true
|
||||
else
|
||||
#echo "Nope project NOT affected save time"
|
||||
echo false
|
||||
fi;
|
||||
|
||||
if [[ $isAffected == false ]]; then
|
||||
# echo "Read the deps as array"
|
||||
IFS=',' read -ra depsArray <<< "$deps"
|
||||
|
||||
#echo "Loop through the deps to check if they are affected"
|
||||
for i in "${depsArray[@]}"
|
||||
do
|
||||
if [[ $AFFECTED_LIBS =~ $i ]]; then
|
||||
isAffected=true;
|
||||
break
|
||||
fi;
|
||||
done;
|
||||
fi;
|
||||
|
||||
echo $isAffected;
|
||||
|
||||
@@ -7,22 +7,21 @@ cd $DIR/../../../
|
||||
|
||||
BASE_DIRECTORY=$(echo "$FOLDER" | cut -d "/" -f1)
|
||||
verifyLib=$1;
|
||||
deps=$2;
|
||||
REGEX="(repository|workflow)_dispatch"
|
||||
|
||||
# set test-e2e params
|
||||
if [ -n "$3" ]; then
|
||||
e2eParams="--$3"
|
||||
if [ -n "$2" ]; then
|
||||
e2eParams="--$2"
|
||||
else
|
||||
e2eParams=""
|
||||
fi
|
||||
|
||||
echo "Step1 - Verify if affected libs contains $verifyLib or if deps $deps are affected"
|
||||
echo "Step1 - Verify if affected libs contains $verifyLib"
|
||||
|
||||
AFFECTED_LIB=$(./scripts/github/affected-contains.sh $verifyLib $deps)
|
||||
AFFECTED_LIB=$(./scripts/github/affected-contains.sh $verifyLib )
|
||||
|
||||
if [ ${AFFECTED_LIB} == true ]; then
|
||||
echo "Step2 - $verifyLib OR deps $deps affected... will execute e2e"
|
||||
echo "Step2 - $verifyLib affected... will execute e2e"
|
||||
|
||||
if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then
|
||||
echo "Calculate affected e2e $BASE_HASH $HEAD_HASH"
|
||||
@@ -55,6 +54,6 @@ if [ ${AFFECTED_LIB} == true ]; then
|
||||
fi;
|
||||
|
||||
else
|
||||
echo "Step2 - Lib $verifyLib OR deps $deps NOT affected. No need to run e2e"
|
||||
echo "Step2 - Lib $verifyLib NOT affected. No need to run e2e"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user