diff --git a/.gitignore b/.gitignore index e95c71a25d..a8b1999fa9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.angular/cache *.log node_modules bundles @@ -30,3 +31,5 @@ out-tsc e2e-result-* licenses.txt .DS_Store + +.angular diff --git a/.storybook/tsconfig.json b/.storybook/tsconfig.json index ee045f6496..54a658ae74 100644 --- a/.storybook/tsconfig.json +++ b/.storybook/tsconfig.json @@ -1,10 +1,10 @@ { - "extends": "../tsconfig.json", - "exclude": [ - "../**/*.spec.js", - "../**/*.spec.ts", - "../**/*.spec.tsx", - "../**/*.spec.jsx" - ], - "include": ["../**/*"] + "extends": "../tsconfig.json", + "exclude": [ + "../**/*.spec.js", + "../**/*.spec.ts", + "../**/*.spec.tsx", + "../**/*.spec.jsx" + ], + "include": ["../**/*"] } diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js deleted file mode 100644 index 69fcea7697..0000000000 --- a/.storybook/webpack.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Export a function. Accept the base config as the only param. - * @param {Object} options - * @param {Required} options.config - * @param {'DEVELOPMENT' | 'PRODUCTION'} options.mode - change the build configuration. 'PRODUCTION' is used when building the static version of storybook. - */ -module.exports = async ({ config, mode }) => { - // Make whatever fine-grained changes you need - - // Return the altered config - return config; -}; diff --git a/.stylelintignore b/.stylelintignore index 66f64bc148..e4152edfa6 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -1,2 +1,3 @@ _theming.scss lib/core/viewer/components/pdf-viewer-host.component.scss +lib/dist diff --git a/.travis.yml b/.travis.yml index fbd6878b3c..27b3cd30e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,12 @@ branches: only: - /^master(-patch.*)?$/ - /^develop(-patch.*)?$/ + - master + - develop +#remove after upgrade + - angular-upgrade-v13 + - /.*old-env.*/ + - /.*next-release.*/ - /.*beta.*/ stages: @@ -61,9 +67,9 @@ stages: - name: "Build Demo shell" if: tag IS blank - name: "Unit test Lib" - if: type = pull_request || (type = cron || type = api) + if: type = pull_request || (type = cron || type = api) || branch = "angular-upgrade-v13" - name: "e2e Test" - if: type = pull_request || (type = cron || type = api) + if: type = pull_request || (type = cron || type = api) || branch = "angular-upgrade-v13" - name: "Release tag" if: branch =~ /^master(-patch.*)?$/ - name: "Deprecate develop builds" @@ -123,7 +129,7 @@ jobs: script: # Build Demo shell & Storybook for production docker" - NODE_OPTIONS=--max_old_space_size=8192 nx build demoshell --configuration production - - NODE_OPTIONS=--max_old_space_size=8192 nx run stories:build-storybook --configuration ci + - NODE_OPTIONS=--max_old_space_size=8192 nx run stories:build-storybook --configuration ci --projectBuildConfig=stories:build-storybook - ./scripts/travis/release/release-docker.sh workspaces: create: @@ -187,11 +193,15 @@ jobs: - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="core" - PROVIDER='ALL' @@ -203,11 +213,15 @@ jobs: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="content-services/components" - PROVIDER="ECM" @@ -219,11 +233,15 @@ jobs: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="content-services/directives" - PROVIDER="ECM" @@ -235,11 +253,15 @@ jobs: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="content-services/document-list" - PROVIDER="ECM" @@ -251,11 +273,15 @@ jobs: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="content-services/metadata" - PROVIDER="ECM" @@ -267,11 +293,15 @@ jobs: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="content-services/upload" - PROVIDER="ECM" @@ -283,11 +313,15 @@ jobs: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="search" - PROVIDER="ECM" @@ -296,15 +330,19 @@ jobs: - stage: "e2e Test" name: "Process: Form" before_script: - - ./scripts/ci/job_hooks/before_e2e.sh || travis_terminate 1 + - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-external-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services/form" - PROVIDER="BPM" @@ -313,15 +351,19 @@ jobs: - stage: "e2e Test" name: "Process: Process" before_script: - - ./scripts/ci/job_hooks/before_e2e.sh || travis_terminate 1 + - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-external-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services/process" - PROVIDER="BPM" @@ -330,15 +372,19 @@ jobs: - stage: "e2e Test" name: "Process: Tasks" before_script: - - ./scripts/ci/job_hooks/before_e2e.sh || travis_terminate 1 + - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-external-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services/tasks" - PROVIDER="BPM" @@ -347,20 +393,36 @@ jobs: - stage: "e2e Test" name: "Process: Widgets" before_script: - - ./scripts/ci/job_hooks/before_e2e.sh || travis_terminate 1 + - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-external-cs-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services/widgets" - PROVIDER="BPM" - AUTH_TYPE="OAUTH" + - stage: "e2e Test" + name: "Process Cloud:playwright" + before_script: + - ./scripts/ci/job_hooks/before_e2e.sh + - ./scripts/ci/jobs/dbpci-before-playwright || travis_terminate 1 + - ./scripts/ci/check-env/check-ps-cloud-env.sh || travis_terminate 1 + script: ./scripts/travis/storybook-testing/storybook-test.sh + workspaces: + use: + - built_libs_cache + - built_demo_shell_cache + - stage: "e2e Test" name: "Process Cloud : Form" before_script: @@ -368,11 +430,15 @@ jobs: - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-ps-cloud-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services-cloud/form-field" - PROVIDER="ALL" @@ -386,11 +452,15 @@ jobs: - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-ps-cloud-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services-cloud/people" - PROVIDER="ALL" @@ -404,11 +474,15 @@ jobs: - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-ps-cloud-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services-cloud/process" - PROVIDER="ALL" @@ -422,11 +496,15 @@ jobs: - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-ps-cloud-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services-cloud/start-task" - PROVIDER="ALL" @@ -440,11 +518,15 @@ jobs: - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 - ./scripts/ci/check-env/check-ps-cloud-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/e2e.sh - after_script: ./scripts/ci/job_hooks/after_e2e.sh workspaces: - use: - - built_libs_cache - - built_demo_shell_cache + create: + name: e2e_cache + paths: + - "$SMART_RUNNER_DIRECTORY" + use: + - built_libs_cache + - built_demo_shell_cache + - e2e_cache env: - FOLDER="process-services-cloud/task-list" - PROVIDER="ALL" diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c96455222c..7714c29670 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,4 @@ { "recommendations": [ - "stylelint.vscode-stylelint", - "editorconfig.editorconfig", ] } diff --git a/README.md b/README.md index 1f5ea291ad..3bc9b02183 100644 --- a/README.md +++ b/README.md @@ -72,4 +72,3 @@ All components are supported in the following browsers: * Due to a [known issue](https://bugzilla.mozilla.org/show_bug.cgi?id=1188880) in Firefox, the Alfresco Upload Component does not currently support folder upload functionality on Firefox. See the [Browser Support](BROWSER-SUPPORT.md) article for more details. - diff --git a/angular.json b/angular.json index 8fc764f4eb..2231a7f656 100644 --- a/angular.json +++ b/angular.json @@ -177,24 +177,27 @@ "node_modules/pdfjs-dist/web/pdf_viewer.js", "node_modules/raphael/raphael.min.js", "node_modules/moment/min/moment.min.js" - ] + ], + "vendorChunk": true, + "extractLicenses": false, + "buildOptimizer": false, + "sourceMap": true, + "optimization": false, + "namedChunks": true }, "configurations": { "production": { "budgets": [ { "type": "anyComponentStyle", - "maximumWarning": "6kb" + "maximumWarning": "12kb" } ], "optimization": true, "outputHashing": "all", "sourceMap": false, - "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, - "vendorChunk": true, "buildOptimizer": true, "verbose": false, "fileReplacements": [ @@ -214,11 +217,8 @@ "optimization": true, "outputHashing": "all", "sourceMap": false, - "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, - "vendorChunk": true, "buildOptimizer": true, "verbose": false, "fileReplacements": [ @@ -228,7 +228,8 @@ } ] } - } + }, + "defaultConfiguration": "" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", @@ -389,29 +390,17 @@ "protractorConfig": "./e2e/protractor.conf.js", "devServerTarget": "lib-e2e-test:serve" } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "./e2e/tsconfig.e2e.json" - ], - "exclude": [ - "**/lib/**/*", - "**/node_modules/**/*" - ] - } } } }, "core": { + "projectType": "library", "root": "lib/core", "sourceRoot": "lib/core", - "projectType": "library", "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "lib/core/tsconfig.lib.json", "project": "lib/core/ng-package.json" @@ -420,13 +409,17 @@ "production": { "project": "lib/core/ng-package.json", "tsConfig": "lib/core/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "lib/core/tsconfig.lib.json" } - } + }, + "defaultConfiguration": "production" }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "main": "lib/core/test.ts", + "main": "lib/core/src/test.ts", "tsConfig": "lib/core/tsconfig.spec.json", "karmaConfig": "lib/core/karma.conf.js", "sourceMap": true, @@ -486,7 +479,7 @@ "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "lib/content-services/tsconfig.lib.json", "project": "lib/content-services/ng-package.json" @@ -558,7 +551,7 @@ "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "lib/process-services/tsconfig.lib.json", "project": "lib/process-services/ng-package.json" @@ -597,7 +590,7 @@ "prefix": "adf-cloud", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "lib/process-services-cloud/tsconfig.lib.json", "project": "lib/process-services-cloud/ng-package.json" @@ -669,7 +662,7 @@ "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "lib/insights/tsconfig.lib.json", "project": "lib/insights/ng-package.json" @@ -708,7 +701,7 @@ "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "lib/extensions/tsconfig.lib.json", "project": "lib/extensions/ng-package.json" @@ -746,13 +739,21 @@ "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@nrwl/node:webpack", "options": { - "tsConfig": "lib/testing/tsconfig.lib.json", + "projectRoot": "lib/testing", + "outputPath": "lib/dist/testing", + "main": "lib/testing/index.ts", + "generatePackageJson" : true, + "tsConfig": "lib/testing/tsconfig.lib.prod.json", "project": "lib/testing/ng-package.json" }, "configurations": { "production": { + "projectRoot": "lib/testing", + "outputPath": "lib/dist/testing", + "main": "lib/testing/index.ts", + "generatePackageJson" : true, "tsConfig": "lib/testing/tsconfig.lib.prod.json" } } @@ -775,13 +776,15 @@ "prefix": "adf", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@nrwl/workspace:run-commands", "options": { - "tsConfig": "lib/cli/tsconfig.json" + "commands": [ + "cd lib/cli && npm i && npm run dist" + ] }, "configurations": { "production": { - "tsConfig": "lib/cli/tsconfig.lib.prod.json" + "tsConfig": "lib/cli/tsconfig.json" } } }, diff --git a/demo-shell/e2e/tsconfig.e2e.json b/demo-shell/e2e/tsconfig.e2e.json index 69ed26a1b4..8ad53b9929 100644 --- a/demo-shell/e2e/tsconfig.e2e.json +++ b/demo-shell/e2e/tsconfig.e2e.json @@ -4,10 +4,6 @@ "outDir": "../out-tsc/e2e", "module": "commonjs", "target": "es5", - "types":[ - "jasmine", - "jasminewd2", - "node" - ] + "types": ["jasmine", "jasminewd2", "node"] } } diff --git a/demo-shell/src/app/components/aspect-list-sample/aspect-list-sample.component.ts b/demo-shell/src/app/components/aspect-list-sample/aspect-list-sample.component.ts index e9f08a2d44..766cced546 100644 --- a/demo-shell/src/app/components/aspect-list-sample/aspect-list-sample.component.ts +++ b/demo-shell/src/app/components/aspect-list-sample/aspect-list-sample.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AspectListService } from '@alfresco/adf-content-services'; +import { DialogAspectListService } from '@alfresco/adf-content-services'; import { Component } from '@angular/core'; @Component({ @@ -30,14 +30,14 @@ export class AspectListSampleComponent { currentResult: string[] = []; - constructor(private aspectListService: AspectListService) { } + constructor(private dialogAspectListService: DialogAspectListService) { } showAspectForNode() { this.isShowed = !this.isShowed; } openAspectDialog() { - this.aspectListService.openAspectListDialog(this.currentNodeId).subscribe((result) => this.currentResult = Array.from(result)); + this.dialogAspectListService.openAspectListDialog(this.currentNodeId).subscribe((result) => this.currentResult = Array.from(result)); } onValueChanged(aspects) { diff --git a/demo-shell/src/app/components/files/files.component.ts b/demo-shell/src/app/components/files/files.component.ts index 98199965c5..7dcfef8379 100644 --- a/demo-shell/src/app/components/files/files.component.ts +++ b/demo-shell/src/app/components/files/files.component.ts @@ -38,7 +38,7 @@ import { LibraryDialogComponent, ContentMetadataService, FilterSearch, - AspectListService + DialogAspectListService } from '@alfresco/adf-content-services'; import { SelectAppsDialogComponent, ProcessFormRenderingService } from '@alfresco/adf-process-services'; @@ -233,7 +233,7 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { public alfrescoApiService: AlfrescoApiService, private contentMetadataService: ContentMetadataService, private sharedLinksApiService: SharedLinksApiService, - private aspectListService: AspectListService, + private dialogAspectListService: DialogAspectListService, private nodeService: NodesApiService) { } @@ -474,7 +474,7 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { } onAspectUpdate(event: any) { - this.aspectListService.openAspectListDialog(event.value.entry.id).subscribe((aspectList) => { + this.dialogAspectListService.openAspectListDialog(event.value.entry.id).subscribe((aspectList) => { this.nodeService.updateNode(event.value.entry.id, {aspectNames : [...aspectList]}).subscribe(() => { this.openSnackMessageInfo('Node Aspects Updated'); }); diff --git a/demo-shell/src/custom-style-dev.scss b/demo-shell/src/custom-style-dev.scss index 497d0dc730..c35d1e11ef 100644 --- a/demo-shell/src/custom-style-dev.scss +++ b/demo-shell/src/custom-style-dev.scss @@ -1,11 +1,11 @@ -@import '~@angular/material/theming'; +@use '@angular/material' as mat; @import '../../lib/core/styles/index'; -@include mat-core($alfresco-typography); +@include mat.core($alfresco-typography); -$primary: mat-palette($alfresco-accent-orange); -$accent: mat-palette($alfresco-accent-purple); -$warn: mat-palette($alfresco-warn); -$theme: mat-light-theme( +$primary: mat.define-palette($alfresco-accent-orange); +$accent: mat.define-palette($alfresco-accent-purple); +$warn: mat.define-palette($alfresco-warn); +$theme: mat.define-light-theme( ( color: ( primary: $primary, @@ -14,7 +14,7 @@ $theme: mat-light-theme( ) ); -@include angular-material-theme($theme); +@include mat.all-component-themes($theme); @include alfresco-material-theme($theme); body, @@ -22,9 +22,9 @@ html { margin: 0; height: 100%; overflow: hidden; - font-size: mat-font-size($alfresco-typography, body-1); - font-family: mat-font-family($alfresco-typography); - line-height: mat-line-height($alfresco-typography, body-1); + font-size: mat.font-size($alfresco-typography, body-1); + font-family: mat.font-family($alfresco-typography); + line-height: mat.line-height($alfresco-typography, body-1); } body { diff --git a/demo-shell/src/custom-style.scss b/demo-shell/src/custom-style.scss index 40bd0fe084..c7cc6f98fd 100644 --- a/demo-shell/src/custom-style.scss +++ b/demo-shell/src/custom-style.scss @@ -1,11 +1,11 @@ -@import '~@angular/material/theming'; +@use '@angular/material' as mat; @import '~@alfresco/adf-core/theming'; -@include mat-core($alfresco-typography); +@include mat.core($alfresco-typography); -$primary: mat-palette($alfresco-accent-orange); -$accent: mat-palette($alfresco-accent-purple); -$warn: mat-palette($alfresco-warn); -$theme: mat-light-theme( +$primary: mat.define-palette($alfresco-accent-orange); +$accent: mat.define-palette($alfresco-accent-purple); +$warn: mat.define-palette($alfresco-warn); +$theme: mat.define-light-theme( ( color: ( primary: $primary, @@ -14,7 +14,7 @@ $theme: mat-light-theme( ) ); -@include angular-material-theme($theme); +@include mat.all-component-themes($theme); @include alfresco-material-theme($theme); body, @@ -22,9 +22,9 @@ html { margin: 0; height: 100%; overflow: hidden; - font-size: mat-font-size($alfresco-typography, body-1); - font-family: mat-font-family($alfresco-typography); - line-height: mat-line-height($alfresco-typography, body-1); + font-size: mat.font-size($alfresco-typography, body-1); + font-family: mat.font-family($alfresco-typography); + line-height: mat.line-height($alfresco-typography, body-1); } body { diff --git a/demo-shell/src/polyfills.ts b/demo-shell/src/polyfills.ts index 60af05a667..b31c92476c 100644 --- a/demo-shell/src/polyfills.ts +++ b/demo-shell/src/polyfills.ts @@ -35,13 +35,10 @@ * BROWSER POLYFILLS */ -/** IE10 and IE11 requires the following for NgClass support on SVG elements */ -// import 'classlist.js'; // Run `npm install --save classlist.js`. - /*************************************************************************************************** * Zone JS is required by Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /** * Support custom event in IE11 diff --git a/demo-shell/src/test.ts b/demo-shell/src/test.ts index b5b7ec32d1..b4f5f1fe3b 100644 --- a/demo-shell/src/test.ts +++ b/demo-shell/src/test.ts @@ -17,7 +17,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, @@ -29,7 +29,9 @@ declare const require: any; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting() + platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false } +} ); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); diff --git a/e2e/content-services/directives/delete-directive.e2e.ts b/e2e/content-services/directives/delete-directive.e2e.ts index fdc3ef5623..ce5f5aabda 100644 --- a/e2e/content-services/directives/delete-directive.e2e.ts +++ b/e2e/content-services/directives/delete-directive.e2e.ts @@ -149,7 +149,7 @@ describe('Delete Directive', () => { await contentListPage.dataTable.checkContentIsNotDisplayed('Display name', folderInfo.name); }); - it('[C260193] Delete file when different selections are set', async () => { + it('[C260193] Delete file when different selections are set', async () => { await contentServicesPage.chooseSelectionMode('None'); await contentListPage.selectRow(txtFileModel.name); await contentListPage.dataTable.checkRowIsNotSelected('Display name', txtFileModel.name); diff --git a/e2e/content-services/metadata/metadata-content-type.e2e.ts b/e2e/content-services/metadata/metadata-content-type.e2e.ts index 186034ceb3..7096b42323 100644 --- a/e2e/content-services/metadata/metadata-content-type.e2e.ts +++ b/e2e/content-services/metadata/metadata-content-type.e2e.ts @@ -109,7 +109,7 @@ describe('content type', () => { }); it('[C593560] Should the user be able to select a new content type and save it only after the confirmation dialog', async () => { - await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${pdfFile.id}/view)`); + await BrowserActions.getUrl(browser.baseUrl + `/files(overlay:files/${pdfFile.id}/view)`); await viewerPage.checkFileIsLoaded(pdfFile.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); @@ -134,7 +134,7 @@ describe('content type', () => { await loginPage.login(acsUser.username, acsUser.password); await navigationBarPage.navigateToContentServices(); await contentServicesPage.contentList.dataTablePage().waitTillContentLoaded(); - await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${pdfFile.id}/view)`); + await BrowserActions.getUrl(browser.baseUrl + `/files(overlay:files/${pdfFile.id}/view)`); await viewerPage.checkFileIsLoaded(pdfFile.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); @@ -150,7 +150,7 @@ describe('content type', () => { }); it('[C593559] Should the user be able to select a new content type and not save it when press cancel in the confirmation dialog', async () => { - await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${docxFileModel.id}/view)`); + await BrowserActions.getUrl(browser.baseUrl + `/files(overlay:files/${docxFileModel.id}/view)`); await viewerPage.checkFileIsLoaded(docxFileModel.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); @@ -173,7 +173,7 @@ describe('content type', () => { await loginPage.login(acsUser.username, acsUser.password); await navigationBarPage.navigateToContentServices(); await contentServicesPage.contentList.dataTablePage().waitTillContentLoaded(); - await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${docxFileModel.id}/view)`); + await BrowserActions.getUrl(browser.baseUrl + `/files(overlay:files/${docxFileModel.id}/view)`); await viewerPage.checkFileIsLoaded(docxFileModel.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); diff --git a/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts b/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts index 722643c578..c439b84e23 100644 --- a/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts +++ b/e2e/content-services/metadata/metadata-smoke-tests.e2e.ts @@ -269,7 +269,7 @@ describe('Metadata component', () => { it('[C279960] Should show the last username modifier when modify a File', async () => { await loginPage.loginWithProfile('admin'); - await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${pngFileModel.id}/view)`); + await BrowserActions.getUrl(browser.baseUrl + `/files(overlay:files/${pngFileModel.id}/view)`); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); diff --git a/e2e/core/pages/content-services.page.ts b/e2e/core/pages/content-services.page.ts index c9fd712605..288a573971 100644 --- a/e2e/core/pages/content-services.page.ts +++ b/e2e/core/pages/content-services.page.ts @@ -93,7 +93,7 @@ export class ContentServicesPage { notMarkedFavorite = element(by.cssContainingText('button[data-automation-id="favorite"] mat-icon', 'star_border')); multiSelectToggle = $('[data-automation-id="multiSelectToggle"]'); selectAllCheckbox = $$('.adf-checkbox-sr-only').first(); - selectionModeDropdown = $('.mat-select[aria-label="Selection Mode"]'); + selectionModeDropdown = $('.mat-select[placeholder="Selection Mode"]'); selectedNodesList = $$('.app-content-service-settings li'); siteListDropdown = new DropdownPage($(`mat-select[data-automation-id='site-my-files-option']`)); sortingDropdown = new DropdownPage($('mat-select[data-automation-id="grid-view-sorting"]')); diff --git a/e2e/core/pages/login-shell.page.ts b/e2e/core/pages/login-shell.page.ts index ea9dd6dc8b..c596439de6 100644 --- a/e2e/core/pages/login-shell.page.ts +++ b/e2e/core/pages/login-shell.page.ts @@ -32,8 +32,8 @@ export class LoginShellPage { usernameError = $('span[data-automation-id="username-error"]'); passwordError = $('span[data-automation-id="password-required"]'); loginError = $('.adf-login-error-message'); - usernameInactive = $('input[id="username"][aria-invalid="false"]'); - passwordInactive = $('input[id="password"][aria-invalid="false"]'); + usernameInactive = $('input[id="username"][class*="ng-invalid"]'); + passwordInactive = $('input[id="password"][class*="ng-invalid"]'); adfLogo = $('.adf-img-logo'); usernameHighlighted = $('input[id="username"][aria-invalid="true"]'); @@ -115,6 +115,7 @@ export class LoginShellPage { async checkPasswordHighlighted(): Promise { await BrowserActions.click(this.adfLogo); + await browser.sleep(900000); await BrowserVisibility.waitUntilElementIsVisible(this.passwordHighlighted); } diff --git a/e2e/process-services-cloud/pages/people-group-cloud-component.page.ts b/e2e/process-services-cloud/pages/people-group-cloud-component.page.ts index b95a9e0615..6ad827fe6b 100644 --- a/e2e/process-services-cloud/pages/people-group-cloud-component.page.ts +++ b/e2e/process-services-cloud/pages/people-group-cloud-component.page.ts @@ -33,7 +33,7 @@ export class PeopleGroupCloudComponentPage { groupAppInput = $('input[data-automation-id="app-group-app-input"]'); peopleCloudComponentTitle = element(by.cssContainingText('mat-card-title', 'People Cloud Component')); groupCloudComponentTitle = element(by.cssContainingText('mat-card-title', 'Groups Cloud Component')); - preselectValidation = $$('mat-checkbox.app-preselect-value').first(); + preselectValidation = $$('mat-checkbox.app-preselect-value label').first(); preselectValidationStatus = $$('mat-checkbox.app-preselect-value label input').first(); async navigateTo() { diff --git a/e2e/process-services-cloud/people/people-group-cloud-component.e2e.ts b/e2e/process-services-cloud/people/people-group-cloud-component.e2e.ts index f26c06f5cc..b9e302a23b 100644 --- a/e2e/process-services-cloud/people/people-group-cloud-component.e2e.ts +++ b/e2e/process-services-cloud/people/people-group-cloud-component.e2e.ts @@ -29,220 +29,224 @@ import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; describe('People Groups Cloud Component', () => { - const loginSSOPage = new LoginPage(); - const navigationBarPage = new NavigationBarPage(); - const peopleGroupCloudComponentPage = new PeopleGroupCloudComponentPage(); - const peopleCloudComponent = new PeopleCloudComponentPage(); - const groupCloudComponentPage = new GroupCloudComponentPage(); + describe('People Groups Cloud Component', () => { - const apiService = createApiService(); - const identityService = new IdentityService(apiService); - const rolesService = new RolesService(apiService); - const groupIdentityService = new GroupIdentityService(apiService); + const loginSSOPage = new LoginPage(); + const navigationBarPage = new NavigationBarPage(); + const peopleGroupCloudComponentPage = new PeopleGroupCloudComponentPage(); + const peopleCloudComponent = new PeopleCloudComponentPage(); + const groupCloudComponentPage = new GroupCloudComponentPage(); - let apsUser; - let testUser; - let devopsUser; - let activitiUser; - let multipleRolesUser; - let noRoleUser; - let groupUser; - let groupAdmin; - let groupNoRole; - let groupMultipleRoles; - let apsUserRoleId: string; - let apsAdminRoleId: string; - let users = []; - let groups = []; + const apiService = createApiService(); + const identityService = new IdentityService(apiService); + const rolesService = new RolesService(apiService); + const groupIdentityService = new GroupIdentityService(apiService); - beforeAll(async () => { - await apiService.loginWithProfile('identityAdmin'); + let apsUser; + let testUser; + let devopsUser; + let activitiUser; + let noRoleUser; + let groupUser; + let groupAdmin; + let groupNoRole; + let apsUserRoleId: string; + let apsAdminRoleId: string; + let users = []; + let groups = []; - testUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); - apsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); - activitiUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); - devopsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_DEVOPS]); - multipleRolesUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER, identityService.ROLES.ACTIVITI_ADMIN]); - noRoleUser = await identityService.createIdentityUser(); + beforeAll(async () => { + await apiService.loginWithProfile('identityAdmin'); - apsAdminRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_ADMIN); - apsUserRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_USER); + testUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); + apsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); + activitiUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); + devopsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_DEVOPS]); + noRoleUser = await identityService.createIdentityUser(); - groupUser = await groupIdentityService.createIdentityGroup(); - await groupIdentityService.assignRole(groupUser.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER); + apsAdminRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_ADMIN); + apsUserRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_USER); - groupAdmin = await groupIdentityService.createIdentityGroup(); - await groupIdentityService.assignRole(groupAdmin.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN); + groupUser = await groupIdentityService.createIdentityGroup(); + await groupIdentityService.assignRole(groupUser.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER); - groupMultipleRoles = await groupIdentityService.createIdentityGroup(); - await groupIdentityService.assignRole(groupMultipleRoles.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN); - await groupIdentityService.assignRole(groupMultipleRoles.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER); + groupAdmin = await groupIdentityService.createIdentityGroup(); + await groupIdentityService.assignRole(groupAdmin.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN); - groupNoRole = await groupIdentityService.createIdentityGroup(); + groupNoRole = await groupIdentityService.createIdentityGroup(); - users = [`${apsUser.idIdentityService}`, `${activitiUser.idIdentityService}`, `${noRoleUser.idIdentityService}`, - `${testUser.idIdentityService}`, `${devopsUser.idIdentityService}`, `${multipleRolesUser.idIdentityService}`]; - groups = [`${groupUser.id}`, `${groupAdmin.id}`, `${groupNoRole.id}`, `${groupMultipleRoles.id}`]; + users = [`${apsUser.idIdentityService}`, `${activitiUser.idIdentityService}`, `${noRoleUser.idIdentityService}`, + `${testUser.idIdentityService}`, `${devopsUser.idIdentityService}`]; + groups = [`${groupUser.id}`, `${groupAdmin.id}`, `${groupNoRole.id}`]; - await loginSSOPage.login(testUser.username, testUser.password); - }); + await loginSSOPage.login(testUser.username, testUser.password); + }); - afterAll(async () => { - await apiService.loginWithProfile('identityAdmin'); - for (const user of users) { - await identityService.deleteIdentityUser(user); - } - for (const group of groups) { - await groupIdentityService.deleteIdentityGroup(group); - } - }); + afterAll(async () => { + await apiService.loginWithProfile('identityAdmin'); + for (const user of users) { + await identityService.deleteIdentityUser(user); + } + for (const group of groups) { + await groupIdentityService.deleteIdentityGroup(group); + } + }); - beforeEach(async () => { - await navigationBarPage.navigateToPeopleGroupCloudPage(); - await peopleGroupCloudComponentPage.checkGroupsCloudComponentTitleIsDisplayed(); - await peopleGroupCloudComponentPage.checkPeopleCloudComponentTitleIsDisplayed(); - }); - - afterEach(async () => { - await browser.refresh(); - }); - - describe('[C297674] Should be able to add filtering to People Cloud Component', () => { beforeEach(async () => { + await navigationBarPage.navigateToPeopleGroupCloudPage(); + await peopleGroupCloudComponentPage.checkGroupsCloudComponentTitleIsDisplayed(); + await peopleGroupCloudComponentPage.checkPeopleCloudComponentTitleIsDisplayed(); + }); + + afterEach(async () => { + await browser.refresh(); + }); + + describe('[C297674] Should be able to add filtering to People Cloud Component', () => { + beforeEach(async () => { + await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection(); + await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected(); + await peopleGroupCloudComponentPage.clickPeopleCloudFilterRole(); + await peopleGroupCloudComponentPage.checkPeopleCloudFilterRole(); + }); + + it('No role filtering', async () => { + await peopleCloudComponent.searchAssignee(noRoleUser.lastName); + await peopleCloudComponent.checkUserIsDisplayed(`${noRoleUser.firstName} ${noRoleUser.lastName}`); + await peopleCloudComponent.searchAssignee(apsUser.lastName); + await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`); + await peopleCloudComponent.searchAssignee(testUser.lastName); + await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`); + }); + + it('One role filtering', async () => { + await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`); + await peopleCloudComponent.searchAssignee(apsUser.lastName); + await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`); + await peopleCloudComponent.searchAssignee(devopsUser.lastName); + await peopleCloudComponent.checkNoResultsFoundError(); + await peopleCloudComponent.searchAssignee(noRoleUser.lastName); + await peopleCloudComponent.checkNoResultsFoundError(); + }); + + it('Multiple roles filtering', async () => { + await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}", "${identityService.ROLES.ACTIVITI_USER}"]`); + await peopleCloudComponent.searchAssignee(apsUser.lastName); + await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`); + await peopleCloudComponent.searchAssignee(testUser.lastName); + await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`); + await peopleCloudComponent.searchAssignee(noRoleUser.lastName); + await peopleCloudComponent.checkNoResultsFoundError(); + }); + }); + + describe('[C309674] Should be able to add filtering to Group Cloud Component', () => { + beforeEach(async () => { + await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection(); + await peopleGroupCloudComponentPage.clickGroupCloudFilterRole(); + }); + + it('No role filtering', async () => { + await peopleGroupCloudComponentPage.clearField(peopleGroupCloudComponentPage.groupRoleInput); + await groupCloudComponentPage.searchGroups(groupNoRole.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupNoRole.name); + await groupCloudComponentPage.searchGroups(groupAdmin.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name); + await groupCloudComponentPage.searchGroups(groupUser.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name); + }); + + it('One role filtering', async () => { + await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}"]`); + await groupCloudComponentPage.searchGroups(groupAdmin.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name); + await groupCloudComponentPage.searchGroups(groupUser.name); + await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name); + await groupCloudComponentPage.checkGroupIsNotDisplayed(groupUser.name); + await groupCloudComponentPage.searchGroups(groupNoRole.name); + await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name); + }); + + it('[C309996] Should be able to filter groups based on composite roles ACTIVITI_USER', async () => { + await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`); + await groupCloudComponentPage.searchGroups(groupAdmin.name); + await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name); + await groupCloudComponentPage.searchGroups(groupNoRole.name); + await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name); + await groupCloudComponentPage.searchGroups(groupUser.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name); + }); + + it('Multiple roles filtering', async () => { + await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}", "${identityService.ROLES.ACTIVITI_USER}"]`); + await groupCloudComponentPage.searchGroups(groupAdmin.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name); + await groupCloudComponentPage.searchGroups(groupUser.name); + await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name); + await groupCloudComponentPage.searchGroups(groupNoRole.name); + await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name); + }); + }); + + it('[C305033] Should fetch the preselect users based on the Validate flag set to True in Single mode selection', async () => { + await peopleGroupCloudComponentPage.clickPeopleCloudSingleSelection(); + await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected(); + + await peopleGroupCloudComponentPage.enterPeoplePreselect('[{"id":"12345","username":"someUsername","email":"someEmail"}]'); + await expect(await peopleCloudComponent.checkSelectedPeople('someUsername')); + + await peopleGroupCloudComponentPage.clickPreselectValidation(); + await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true', 'Validation not present'); + + await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${noRoleUser.idIdentityService}"}]`); + await expect(await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`)); + + await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"}]`); + await expect(await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`)); + + await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${testUser.username}"}]`); + await expect(await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`)); + }); + + it('[C309676] Should fetch the preselect users based on the Validate flag set to True in Multiple mode selection', async () => { await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection(); await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected(); - }); + await peopleGroupCloudComponentPage.clickPreselectValidation(); + await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true'); - it('No role filtering', async () => { - await peopleCloudComponent.searchAssignee(noRoleUser.lastName); - await peopleCloudComponent.checkUserIsDisplayed(`${noRoleUser.firstName} ${noRoleUser.lastName}`); - await peopleCloudComponent.searchAssignee(apsUser.lastName); - await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`); - await peopleCloudComponent.searchAssignee(testUser.lastName); - await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`); - }); + await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${apsUser.idIdentityService}"},{"id":"${testUser.idIdentityService}"},` + + `{"id":"${noRoleUser.idIdentityService}"}]`); + await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`); + await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`); + await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`); + + await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"},{"email":"${testUser.email}"},{"email":"${noRoleUser.email}"}]`); + await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`); + await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`); + await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`); + + await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${apsUser.username}"},{"username":"${testUser.username}"},` + + `{"username":"${noRoleUser.username}"}]`); + await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`); + await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`); + await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`); - it('One role filtering', async () => { - await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`); - await peopleCloudComponent.searchAssignee(apsUser.lastName); - await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`); - await peopleCloudComponent.searchAssignee(devopsUser.lastName); - await peopleCloudComponent.checkNoResultsFoundError(); await peopleCloudComponent.searchAssignee(noRoleUser.lastName); await peopleCloudComponent.checkNoResultsFoundError(); }); - it('Multiple roles filtering', async () => { - await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}", "${identityService.ROLES.ACTIVITI_ADMIN}"]`); - await peopleCloudComponent.searchAssignee(multipleRolesUser.lastName); - await peopleCloudComponent.checkUserIsDisplayed(`${multipleRolesUser.firstName} ${multipleRolesUser.lastName}`); - await peopleCloudComponent.searchAssignee(apsUser.lastName); - await peopleCloudComponent.checkUserIsNotDisplayed(`${apsUser.firstName} ${apsUser.lastName}`); - await peopleCloudComponent.searchAssignee(testUser.lastName); - await peopleCloudComponent.checkUserIsNotDisplayed(`${testUser.firstName} ${testUser.lastName}`); - await peopleCloudComponent.searchAssignee(noRoleUser.lastName); - await peopleCloudComponent.checkNoResultsFoundError(); + it('[C309677] Should populate the Users without any validation when the Preselect flag is set to false', async () => { + await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection(); + await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected(); + await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('false'); + + await peopleGroupCloudComponentPage.enterPeoplePreselect( + `[{"id":"TestId1","firstName":"TestFirstName1","lastName":"TestLastName1"},` + + `{"id":"TestId2","firstName":"TestFirstName2","lastName":"TestLastName2"},` + + `{"id":"TestId3","firstName":"TestFirstName3","lastName":"TestLastName3"}]`); + await peopleCloudComponent.checkSelectedPeople('TestFirstName1 TestLastName1'); + await peopleCloudComponent.checkSelectedPeople('TestFirstName2 TestLastName2'); + await peopleCloudComponent.checkSelectedPeople('TestFirstName3 TestLastName3'); }); }); - - describe('[C309674] Should be able to add filtering to Group Cloud Component', () => { - beforeEach(async () => { - await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection(); - }); - - it('No role filtering', async () => { - await peopleGroupCloudComponentPage.clearField(peopleGroupCloudComponentPage.groupRoleInput); - await groupCloudComponentPage.searchGroups(groupNoRole.name); - await groupCloudComponentPage.checkGroupIsDisplayed(groupNoRole.name); - await groupCloudComponentPage.searchGroups(groupAdmin.name); - await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name); - await groupCloudComponentPage.searchGroups(groupUser.name); - await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name); - }); - - it('One role filtering', async () => { - await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}"]`); - await groupCloudComponentPage.searchGroups(groupAdmin.name); - await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name); - await groupCloudComponentPage.searchGroups(groupUser.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupUser.name); - await groupCloudComponentPage.searchGroups(groupNoRole.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name); - }); - - it('[C309996] Should be able to filter groups based on composite roles ACTIVITI_USER', async () => { - await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`); - await groupCloudComponentPage.searchGroups(groupAdmin.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name); - await groupCloudComponentPage.searchGroups(groupNoRole.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name); - await groupCloudComponentPage.searchGroups(groupUser.name); - await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name); - }); - - it('Multiple roles filtering', async () => { - await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}", "${identityService.ROLES.ACTIVITI_USER}"]`); - await groupCloudComponentPage.searchGroups(groupMultipleRoles.name); - await groupCloudComponentPage.checkGroupIsDisplayed(groupMultipleRoles.name); - await groupCloudComponentPage.searchGroups(groupAdmin.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name); - await groupCloudComponentPage.searchGroups(groupUser.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupUser.name); - await groupCloudComponentPage.searchGroups(groupNoRole.name); - await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name); - }); - }); - - it('[C305033] Should fetch the preselect users based on the Validate flag set to True in Single mode selection', async () => { - await peopleGroupCloudComponentPage.clickPeopleCloudSingleSelection(); - await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected(); - - await peopleGroupCloudComponentPage.enterPeoplePreselect('[{"id":"12345","username":"someUsername","email":"someEmail"}]'); - await expect(await peopleCloudComponent.checkSelectedPeople('someUsername')); - - await peopleGroupCloudComponentPage.clickPreselectValidation(); - await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true'); - - await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"}]`); - await expect(await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`)); - - await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${testUser.username}"}]`); - await expect(await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`)); - }); - - it('[C309676] Should fetch the preselect users based on the Validate flag set to True in Multiple mode selection', async () => { - await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection(); - await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected(); - await peopleGroupCloudComponentPage.clickPreselectValidation(); - await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true'); - - await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"},{"email":"${testUser.email}"},{"email":"${noRoleUser.email}"}]`); - await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`); - await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`); - await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`); - - await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${apsUser.username}"},{"username":"${testUser.username}"},` + - `{"username":"${noRoleUser.username}"}]`); - await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`); - await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`); - await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`); - - await peopleCloudComponent.searchAssignee(noRoleUser.lastName); - await peopleCloudComponent.checkNoResultsFoundError(); - }); - - it('[C309677] Should populate the Users without any validation when the Preselect flag is set to false', async () => { - await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection(); - await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected(); - await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('false'); - - await peopleGroupCloudComponentPage.enterPeoplePreselect( - `[{"id":"TestId1","firstName":"TestFirstName1","lastName":"TestLastName1"},` + - `{"id":"TestId2","firstName":"TestFirstName2","lastName":"TestLastName2"},` + - `{"id":"TestId3","firstName":"TestFirstName3","lastName":"TestLastName3"}]`); - await peopleCloudComponent.checkSelectedPeople('TestFirstName1 TestLastName1'); - await peopleCloudComponent.checkSelectedPeople('TestFirstName2 TestLastName2'); - await peopleCloudComponent.checkSelectedPeople('TestFirstName3 TestLastName3'); - }); }); diff --git a/e2e/resources/activiti7/simpleapp.zip b/e2e/resources/activiti7/simpleapp.zip index cc66adf289..d1e2705731 100644 Binary files a/e2e/resources/activiti7/simpleapp.zip and b/e2e/resources/activiti7/simpleapp.zip differ diff --git a/e2e/search/search-filters.e2e.ts b/e2e/search/search-filters.e2e.ts index c17367d818..a4e0ae5139 100644 --- a/e2e/search/search-filters.e2e.ts +++ b/e2e/search/search-filters.e2e.ts @@ -20,7 +20,8 @@ import { SearchFiltersPage } from './pages/search-filters.page'; import { SearchResultsPage } from './pages/search-results.page'; import { FileModel } from '../models/ACS/file.model'; import { NavigationBarPage } from '../core/pages/navigation-bar.page'; -import { createApiService, +import { + createApiService, BrowserActions, DocumentListPage, LocalStorageUtil, @@ -104,6 +105,9 @@ describe('Search Filters', () => { await browser.sleep(browser.params.testConfig.timeouts.index_search); // wait search index previous file/folder uploaded + }); + + beforeEach(async () => { jsonFile = SearchConfiguration.getConfiguration(); }); @@ -118,6 +122,37 @@ describe('Search Filters', () => { await navigationBarPage.clickLogoutButton(); }); + it('[C291980] Should group search facets under specified labels', async () => { + const currentYear = moment().year(); + + jsonFile.facetQueries.queries[0] = { + 'query': `created:${currentYear}`, + 'label': 'SEARCH.FACET_QUERIES.CREATED_THIS_YEAR' + }; + jsonFile.facetQueries.queries[1] = { + 'query': `content.mimetype:text/html`, + 'label': 'SEARCH.FACET_QUERIES.MIMETYPE', + 'group': 'Type facet queries' + }; + jsonFile.facetQueries.queries[2] = { + 'query': `content.size:[0 TO 10240]`, + 'label': 'SEARCH.FACET_QUERIES.XTRASMALL', + 'group': 'Size facet queries' + }; + + + await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); + + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); + await searchResults.dataTable.waitTillContentLoaded(); + + await searchFiltersPage.checkDefaultFacetQueryGroupIsDisplayed(); + await searchFiltersPage.checkTypeFacetQueryGroupIsDisplayed(); + + await searchFiltersPage.checkSizeFacetQueryGroupIsDisplayed(); + }); + it('[C286298] Should be able to cancel a filter using "x" button from the toolbar', async () => { await searchBarPage.checkSearchIconIsVisible(); await searchBarPage.clickOnSearchIcon(); @@ -176,25 +211,6 @@ describe('Search Filters', () => { }); }); - it('[C291980] Should group search facets under specified labels', async () => { - const currentYear = moment().year(); - - jsonFile.facetQueries.queries[0] = {'query': `created:${currentYear}`, 'label': 'SEARCH.FACET_QUERIES.CREATED_THIS_YEAR'}; - jsonFile.facetQueries.queries[1] = {'query': `content.mimetype:text/html`, 'label': 'SEARCH.FACET_QUERIES.MIMETYPE', 'group': 'Type facet queries'}; - jsonFile.facetQueries.queries[2] = {'query': `content.size:[0 TO 10240]`, 'label': 'SEARCH.FACET_QUERIES.XTRASMALL', 'group': 'Size facet queries'}; - - await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - - await searchBarPage.clickOnSearchIcon(); - await searchBarPage.enterTextAndPressEnter('*'); - await searchResults.dataTable.waitTillContentLoaded(); - - await searchFiltersPage.checkDefaultFacetQueryGroupIsDisplayed(); - await searchFiltersPage.checkTypeFacetQueryGroupIsDisplayed(); - - await searchFiltersPage.checkSizeFacetQueryGroupIsDisplayed(); - }); - it('[C297509] Should display search intervals under specified labels from config', async () => { await searchBarPage.clickOnSearchIcon(); await searchBarPage.enterTextAndPressEnter('*'); diff --git a/e2e/test.config.js b/e2e/test.config.js index e67e524f90..72292dd3c6 100644 --- a/e2e/test.config.js +++ b/e2e/test.config.js @@ -7,7 +7,7 @@ require('dotenv').config({path: process.env.ENV_FILE}); const HOST = process.env.URL_HOST_ADF; -const LOG = process.env.LOG; +const LOG = process.env.E2E_LOG_LEVEL; const HOST_ECM = process.env.PROXY_HOST_ECM || HOST || 'ecm'; const HOST_BPM = process.env.PROXY_HOST_BPM || HOST || 'bpm'; diff --git a/e2e/util/resources.js b/e2e/util/resources.js index dfff18362d..32d8400439 100644 --- a/e2e/util/resources.js +++ b/e2e/util/resources.js @@ -22,7 +22,7 @@ * @class util.Resources */ var path = require('path'); -const ACTIVITI_CLOUD_APPS = require('../../lib/dist/testing/src'); +const ACTIVITI_CLOUD_APPS = require('../../lib/dist/testing'); const RESOURCES = { ...ACTIVITI_CLOUD_APPS, diff --git a/lib/cli/tsconfig.lib.prod.json b/lib/cli/tsconfig.lib.prod.json index 04c0e66277..2a2faa884c 100644 --- a/lib/cli/tsconfig.lib.prod.json +++ b/lib/cli/tsconfig.lib.prod.json @@ -4,6 +4,6 @@ "declarationMap": false }, "angularCompilerOptions": { - "enableIvy": false + "compilationMode": "partial" } } diff --git a/lib/config/app.config.json b/lib/config/app.config.json new file mode 100644 index 0000000000..729fb251d2 --- /dev/null +++ b/lib/config/app.config.json @@ -0,0 +1,7 @@ +{ + "description" : "this config file is used in the unit test", + "ecmHost": "http://{hostname}:{port}/ecm", + "bpmHost": "http://{hostname}:{port}/bpm", + "baseShareUrl": null, + "logLevel" : "silent" +} diff --git a/lib/config/webpack.style.js b/lib/config/webpack.style.js new file mode 100644 index 0000000000..a76983a4aa --- /dev/null +++ b/lib/config/webpack.style.js @@ -0,0 +1,38 @@ +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const path = require("path"); +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); + +module.exports = { + + mode: 'production', + + optimization: { + minimizer: [new CssMinimizerPlugin({})], + }, + + entry: { + 'adf-blue-orange': './lib/core/styles/prebuilt/adf-blue-orange.scss', + 'adf-blue-purple': './lib/core/styles/prebuilt/adf-blue-purple.scss', + 'adf-cyan-orange': './lib/core/styles/prebuilt/adf-cyan-orange.scss', + 'adf-cyan-purple': './lib/core/styles/prebuilt/adf-cyan-purple.scss', + 'adf-green-purple': './lib/core/styles/prebuilt/adf-green-purple.scss', + 'adf-green-orange': './lib/core/styles/prebuilt/adf-green-orange.scss', + 'adf-pink-bluegrey': './lib/core/styles/prebuilt/adf-pink-bluegrey.scss', + 'adf-indigo-pink': './lib/core/styles/prebuilt/adf-indigo-pink.scss', + 'adf-purple-green': './lib/core/styles/prebuilt/adf-purple-green.scss' + }, + + output: { + path: path.resolve(__dirname, '../dist/core/prebuilt-themes/'), + filename: '[name].js', + publicPath: '/dist' + }, + + module: { + rules: [{ + test: /\.scss$/, + use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"] + }] + }, + plugins: [new MiniCssExtractPlugin()] +}; diff --git a/lib/content-services/karma.conf.js b/lib/content-services/karma.conf.js index c18fb3d052..d5878708e4 100644 --- a/lib/content-services/karma.conf.js +++ b/lib/content-services/karma.conf.js @@ -15,7 +15,12 @@ module.exports = function (config) { }, {pattern: 'node_modules/moment/min/moment.min.js', included: true, watched: false}, {pattern: 'lib/content-services/src/lib/i18n/**/en.json', included: false, served: true, watched: false}, - {pattern: 'lib/content-services/src/lib/assets/images/**/*.svg', included: false, served: true, watched: false}, + { + pattern: 'lib/content-services/src/lib/assets/images/**/*.svg', + included: false, + served: true, + watched: false + }, {pattern: 'lib/core/assets/images/ft_ic_folder.svg', included: false, served: true, watched: false}, {pattern: 'lib/core/i18n/**/en.json', included: false, served: true, watched: false}, {pattern: 'lib/content-services/**/*.ts', included: false, served: true, watched: false}, @@ -39,12 +44,15 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma'), require('karma-mocha-reporter') ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, + jasmine: { + random: false + } }, coverageIstanbulReporter: { diff --git a/lib/content-services/ng-package.json b/lib/content-services/ng-package.json index 902dd18567..75ad7aa8bc 100644 --- a/lib/content-services/ng-package.json +++ b/lib/content-services/ng-package.json @@ -8,22 +8,6 @@ ], "lib": { "entryFile": "src/public-api.ts", - "flatModuleFile": "adf-content-services", - "umdModuleIds": { - "@alfresco/js-api": "@alfresco/js-api", - "@angular/platform-browser/animations": "@angular/platform-browser/animations", - "@angular/material": "@angular/material", - "@mat-datetimepicker/core": "@mat-datetimepicker/core", - "@mat-datetimepicker/moment": "@mat-datetimepicker/moment", - "@angular/flex-layout": "@angular/flex-layout", - "@alfresco/adf-core": "@alfresco/adf-core", - "@angular/animations": "@angular/animations", - "@angular/cdk/platform": "@angular/cdk/platform", - "@angular/material/core": "@angular/material/core", - "moment": "moment", - "moment-es6": "moment-es6", - "moment/src/moment": "moment/src/moment", - "@ngx-translate/core": "@ngx-translate/core" - } + "flatModuleFile": "adf-content-services" } } diff --git a/lib/content-services/package.json b/lib/content-services/package.json index c4dc97e1b9..edda0f6ed1 100644 --- a/lib/content-services/package.json +++ b/lib/content-services/package.json @@ -3,7 +3,6 @@ "description": "Alfresco ADF content services", "version": "4.11.0", "author": "Alfresco Software, Ltd.", - "main": "bundles/adf-content-services.js", "repository": { "type": "git", "url": "https://github.com/Alfresco/alfresco-ng2-components.git" diff --git a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts index a455a4f7a4..e4b26c140d 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts @@ -22,10 +22,10 @@ import { TranslateModule } from '@ngx-translate/core'; import { of, Subject } from 'rxjs'; import { ContentTestingModule } from '../testing/content.testing.module'; import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface'; -import { NodesApiService } from 'core'; -import { AspectListService } from './aspect-list.service'; +import { AspectListService } from './services/aspect-list.service'; import { delay } from 'rxjs/operators'; import { AspectEntry, MinimalNode } from '@alfresco/js-api'; +import { NodesApiService } from '@alfresco/adf-core'; const aspectListMock: AspectEntry[] = [{ entry: { diff --git a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts index b2062d3656..aea5423999 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts @@ -18,6 +18,7 @@ import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface'; + @Component({ selector: 'adf-aspect-list-dialog', templateUrl: './aspect-list-dialog.component.html', diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts b/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts index 6a42554b7b..5e79b19a62 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts @@ -20,7 +20,7 @@ import { NodesApiService, setupTestBed } from '@alfresco/adf-core'; import { ContentTestingModule } from '../testing/content.testing.module'; import { TranslateModule } from '@ngx-translate/core'; import { AspectListComponent } from './aspect-list.component'; -import { AspectListService } from './aspect-list.service'; +import { AspectListService } from './services/aspect-list.service'; import { of } from 'rxjs'; import { AspectEntry } from '@alfresco/js-api'; import { delay } from 'rxjs/operators'; diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.ts b/lib/content-services/src/lib/aspect-list/aspect-list.component.ts index 50d7dfb02b..0a6abdc040 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.ts @@ -19,7 +19,7 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsul import { NodesApiService } from '@alfresco/adf-core'; import { Observable, Subject, zip } from 'rxjs'; import { concatMap, map, takeUntil, tap } from 'rxjs/operators'; -import { AspectListService } from './aspect-list.service'; +import { AspectListService } from './services/aspect-list.service'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { AspectEntry } from '@alfresco/js-api'; @Component({ diff --git a/lib/content-services/src/lib/aspect-list/public-api.ts b/lib/content-services/src/lib/aspect-list/public-api.ts index 3c12160357..e05b2cc7af 100644 --- a/lib/content-services/src/lib/aspect-list/public-api.ts +++ b/lib/content-services/src/lib/aspect-list/public-api.ts @@ -17,8 +17,9 @@ export * from './aspect-list.component'; export * from './aspect-list-dialog.component'; -export * from './aspect-list.service'; -export * from './node-aspect.service'; +export * from './services/aspect-list.service'; +export * from './services/node-aspect.service'; +export * from './services/dialog-aspect-list.service'; export * from './aspect-list-dialog-data.interface'; diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.service.ts b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts similarity index 75% rename from lib/content-services/src/lib/aspect-list/aspect-list.service.ts rename to lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts index cb8655459e..60d63545f9 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.service.ts +++ b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts @@ -16,11 +16,8 @@ */ import { Injectable } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core'; -import { from, Observable, of, Subject, zip } from 'rxjs'; -import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface'; -import { AspectListDialogComponent } from './aspect-list-dialog.component'; +import { from, Observable, of, zip } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { AspectEntry, AspectPaging, AspectsApi } from '@alfresco/js-api'; @@ -37,7 +34,6 @@ export class AspectListService { constructor(private alfrescoApiService: AlfrescoApiService, private appConfigService: AppConfigService, - private dialog: MatDialog, private logService: LogService) { } @@ -101,34 +97,4 @@ export class AspectListService { return visibleAspectList; } - openAspectListDialog(nodeId?: string): Observable { - const select = new Subject(); - select.subscribe({ - complete: this.close.bind(this) - }); - - const data: AspectListDialogComponentData = { - title: 'ADF-ASPECT-LIST.DIALOG.TITLE', - description: 'ADF-ASPECT-LIST.DIALOG.DESCRIPTION', - overTableMessage: 'ADF-ASPECT-LIST.DIALOG.OVER-TABLE-MESSAGE', - select, - nodeId - }; - - this.openDialog(data, 'adf-aspect-list-dialog', '750px'); - return select; - } - - private openDialog(data: AspectListDialogComponentData, panelClass: string, width: string) { - this.dialog.open(AspectListDialogComponent, { - data, - panelClass, - width, - disableClose: true - }); - } - - close() { - this.dialog.closeAll(); - } } diff --git a/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts b/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts new file mode 100644 index 0000000000..d0345670d9 --- /dev/null +++ b/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts @@ -0,0 +1,62 @@ +/*! + * @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 { Injectable } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Observable, Subject } from 'rxjs'; +import { AspectListDialogComponentData } from '../aspect-list-dialog-data.interface'; +import { AspectListDialogComponent } from '../aspect-list-dialog.component'; + +@Injectable({ + providedIn: 'root' +}) +export class DialogAspectListService { + + constructor(private dialog: MatDialog) { + } + + openAspectListDialog(nodeId?: string): Observable { + const select = new Subject(); + select.subscribe({ + complete: this.close.bind(this) + }); + + const data: AspectListDialogComponentData = { + title: 'ADF-ASPECT-LIST.DIALOG.TITLE', + description: 'ADF-ASPECT-LIST.DIALOG.DESCRIPTION', + overTableMessage: 'ADF-ASPECT-LIST.DIALOG.OVER-TABLE-MESSAGE', + select, + nodeId + }; + + this.openDialog(data, 'adf-aspect-list-dialog', '750px'); + return select; + } + + private openDialog(data: AspectListDialogComponentData, panelClass: string, width: string) { + this.dialog.open(AspectListDialogComponent, { + data, + panelClass, + width, + disableClose: true + }); + } + + close() { + this.dialog.closeAll(); + } +} diff --git a/lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts b/lib/content-services/src/lib/aspect-list/services/node-aspect.service.spec.ts similarity index 80% rename from lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts rename to lib/content-services/src/lib/aspect-list/services/node-aspect.service.spec.ts index e950ab4fc0..571de395fc 100644 --- a/lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts +++ b/lib/content-services/src/lib/aspect-list/services/node-aspect.service.spec.ts @@ -18,15 +18,15 @@ import { MinimalNode } from '@alfresco/js-api'; import { TestBed } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { AlfrescoApiService, CardViewUpdateService, NodesApiService, setupTestBed } from 'core'; +import { AlfrescoApiService, CardViewUpdateService, NodesApiService, setupTestBed } from '@alfresco/adf-core'; import { of } from 'rxjs'; -import { ContentTestingModule } from '../testing/content.testing.module'; -import { AspectListService } from './aspect-list.service'; +import { ContentTestingModule } from '../../testing/content.testing.module'; import { NodeAspectService } from './node-aspect.service'; +import { DialogAspectListService } from './dialog-aspect-list.service'; describe('NodeAspectService', () => { - let aspectListService: AspectListService; + let dialogAspectListService: DialogAspectListService; let nodeAspectService: NodeAspectService; let nodeApiService: NodesApiService; let alfrescoApiService: AlfrescoApiService; @@ -40,7 +40,7 @@ describe('NodeAspectService', () => { }); beforeEach(() => { - aspectListService = TestBed.inject(AspectListService); + dialogAspectListService = TestBed.inject(DialogAspectListService); nodeAspectService = TestBed.inject(NodeAspectService); nodeApiService = TestBed.inject(NodesApiService); alfrescoApiService = TestBed.inject(AlfrescoApiService); @@ -48,15 +48,15 @@ describe('NodeAspectService', () => { }); it('should open the aspect list dialog', () => { - spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of([])); + spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of([])); spyOn(nodeApiService, 'updateNode').and.returnValue(of(null)); nodeAspectService.updateNodeAspects('fake-node-id'); - expect(aspectListService.openAspectListDialog).toHaveBeenCalledWith('fake-node-id'); + expect(dialogAspectListService.openAspectListDialog).toHaveBeenCalledWith('fake-node-id'); }); it('should update the node when the aspect dialog apply the changes', () => { const expectedParameters = { aspectNames: ['a', 'b', 'c'] }; - spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c'])); + spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c'])); spyOn(nodeApiService, 'updateNode').and.returnValue(of(null)); nodeAspectService.updateNodeAspects('fake-node-id'); expect(nodeApiService.updateNode).toHaveBeenCalledWith('fake-node-id', expectedParameters); @@ -69,7 +69,7 @@ describe('NodeAspectService', () => { done(); }); const fakeNode = new MinimalNode({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] }); - spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c'])); + spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c'])); spyOn(nodeApiService, 'updateNode').and.returnValue(of(fakeNode)); nodeAspectService.updateNodeAspects('fake-node-id'); }); @@ -81,7 +81,7 @@ describe('NodeAspectService', () => { done(); }); const fakeNode = new MinimalNode({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] }); - spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c'])); + spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c'])); spyOn(nodeApiService, 'updateNode').and.returnValue(of(fakeNode)); nodeAspectService.updateNodeAspects('fake-node-id'); }); diff --git a/lib/content-services/src/lib/aspect-list/node-aspect.service.ts b/lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts similarity index 85% rename from lib/content-services/src/lib/aspect-list/node-aspect.service.ts rename to lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts index cc4a1e9b40..3b4e5c3889 100644 --- a/lib/content-services/src/lib/aspect-list/node-aspect.service.ts +++ b/lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { AlfrescoApiService, CardViewUpdateService, NodesApiService } from '@alfresco/adf-core'; -import { AspectListService } from './aspect-list.service'; +import { DialogAspectListService } from './dialog-aspect-list.service'; @Injectable({ providedIn: 'root' @@ -26,12 +26,12 @@ export class NodeAspectService { constructor(private alfrescoApiService: AlfrescoApiService, private nodesApiService: NodesApiService, - private aspectListService: AspectListService, + private dialogAspectListService: DialogAspectListService, private cardViewUpdateService: CardViewUpdateService) { } updateNodeAspects(nodeId: string) { - this.aspectListService.openAspectListDialog(nodeId).subscribe((aspectList) => { + this.dialogAspectListService.openAspectListDialog(nodeId).subscribe((aspectList) => { this.nodesApiService.updateNode(nodeId, { aspectNames: [...aspectList] }).subscribe((updatedNode) => { this.alfrescoApiService.nodeUpdated.next(updatedNode); this.cardViewUpdateService.updateNodeAspect(updatedNode); diff --git a/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts b/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts index 037e47f73a..f68274fdeb 100644 --- a/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts +++ b/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts @@ -28,7 +28,7 @@ import { } from '@angular/core'; import { MatSelect } from '@angular/material/select'; import { Node, PathElementEntity } from '@alfresco/js-api'; -import { DocumentListComponent } from '../document-list'; +import { DocumentListComponent } from '../document-list/components/document-list.component'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts index c7d6173126..a7834ec858 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.spec.ts @@ -24,7 +24,7 @@ import { setupTestBed, AllowableOperationsEnum } from '@alfresco/adf-core'; import { ContentTestingModule } from '../../../testing/content.testing.module'; import { SimpleChange } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; -import { NodeAspectService } from 'content-services/src/lib/aspect-list'; +import { NodeAspectService } from '../../../aspect-list/services/node-aspect.service'; import { ContentMetadataService } from '../../services/content-metadata.service'; import { of } from 'rxjs'; diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts index b509548aa1..6446868507 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts @@ -18,7 +18,7 @@ import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { ContentService, AllowableOperationsEnum, VersionCompatibilityService } from '@alfresco/adf-core'; -import { NodeAspectService } from '../../../aspect-list/node-aspect.service'; +import { NodeAspectService } from '../../../aspect-list/services/node-aspect.service'; import { PresetConfig } from '../../interfaces/content-metadata.interfaces'; @Component({ selector: 'adf-content-metadata-card', diff --git a/lib/content-services/src/lib/content-metadata/services/content-type-property.service.spec.ts b/lib/content-services/src/lib/content-metadata/services/content-type-property.service.spec.ts index 07b848359d..f841348748 100644 --- a/lib/content-services/src/lib/content-metadata/services/content-type-property.service.spec.ts +++ b/lib/content-services/src/lib/content-metadata/services/content-type-property.service.spec.ts @@ -17,7 +17,7 @@ import { TestBed } from '@angular/core/testing'; import { ContentTypePropertiesService } from './content-type-property.service'; -import { CardViewItem, CardViewSelectItemModel, CardViewTextItemModel, setupTestBed, VersionCompatibilityService } from 'core'; +import { CardViewItem, CardViewSelectItemModel, CardViewTextItemModel, setupTestBed, VersionCompatibilityService } from '@alfresco/adf-core'; import { ContentTestingModule } from '../../testing/content.testing.module'; import { TranslateModule } from '@ngx-translate/core'; import { ContentTypeService } from '../../content-type'; diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts new file mode 100644 index 0000000000..d91e68a86c --- /dev/null +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component-search.spec.ts @@ -0,0 +1,787 @@ +/*! + * @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 { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { + MinimalNode, + Node, + NodeEntry, + NodePaging, + RequestScope, + ResultSetPaging, + SiteEntry, + SitePaging +} from '@alfresco/js-api'; +import { + + NodesApiService, + setupTestBed, + SitesService +} from '@alfresco/adf-core'; +import { of } from 'rxjs'; +import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component'; +import { ContentTestingModule } from '../testing/content.testing.module'; +import { DocumentListService } from '../document-list/services/document-list.service'; +import { DocumentListComponent } from '../document-list/components/document-list.component'; +import { CustomResourcesService } from '../document-list/services/custom-resources.service'; +import { NodeEntryEvent, ShareDataRow } from '../document-list'; +import { TranslateModule } from '@ngx-translate/core'; +import { SearchQueryBuilderService } from '../search'; +import { mockQueryBody } from '../mock/search-query.mock'; + +const fakeResultSetPaging: ResultSetPaging = { + list: { + pagination: { + totalItems: 1 + }, + entries: [ + { + entry: { + id: '123', + name: 'MyFolder', + isFile: false, + isFolder: true, + nodeType: 'mock' + } + } + ] + } +}; + +describe('ContentNodeSelectorPanelComponent', () => { + const debounceSearch = 200; + let component: ContentNodeSelectorPanelComponent; + let fixture: ComponentFixture; + let nodeService: NodesApiService; + let sitesService: SitesService; + let searchSpy: jasmine.Spy; + const fakeNodeEntry = new Node({ id: 'fakeId' }); + const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry); + let searchQueryBuilderService: SearchQueryBuilderService; + + const typeToSearchBox = (searchTerm = 'string-to-search') => { + const searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]')); + searchInput.nativeElement.value = searchTerm; + component.searchInput.setValue(searchTerm); + fixture.detectChanges(); + }; + + const triggerSearchResults = (searchResults: ResultSetPaging) => { + component.queryBuilderService.executed.next(searchResults); + }; + + setupTestBed({ + imports: [ + TranslateModule.forRoot(), + ContentTestingModule + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }); + + describe('General component features', () => { + + beforeEach(async () => { + fixture = TestBed.createComponent(ContentNodeSelectorPanelComponent); + component = fixture.componentInstance; + component.debounceSearch = 0; + + nodeService = TestBed.inject(NodesApiService); + sitesService = TestBed.inject(SitesService); + + searchQueryBuilderService = component.queryBuilderService; + component.queryBuilderService.resetToDefaults(); + + spyOn(nodeService, 'getNode').and.returnValue(of(new MinimalNode({ + id: 'fake-node', + path: { elements: [{ nodeType: 'st:site', name: 'fake-site' }] } + }))); + searchSpy = spyOn(searchQueryBuilderService, 'execute'); + const fakeSite = new SiteEntry({ + entry: { + id: 'fake-site', + guid: 'fake-site', + title: 'fake-site', + visibility: 'visible' + } + }); + spyOn(sitesService, 'getSite').and.returnValue(of(fakeSite)); + }); + + afterEach(async () => { + fixture.destroy(); + component = null; + }); + + describe('Search functionality', () => { + let getCorrespondingNodeIdsSpy; + let customResourcesService: CustomResourcesService; + const entry: Node = { id: 'fakeid' } as Node; + + beforeEach(() => { + const documentListService = TestBed.inject(DocumentListService); + const expectedDefaultFolderNode = new NodeEntry(); + component.isSelectionValid = (node: Node) => node.isFile; + + spyOn(documentListService, 'getFolderNode').and.returnValue(of(expectedDefaultFolderNode)); + spyOn(documentListService, 'getFolder').and.returnValue(of(new NodePaging({ + list: { + pagination: {}, + entries: [], + source: {} + } + }))); + + spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } }))); + + customResourcesService = TestBed.inject(CustomResourcesService); + getCorrespondingNodeIdsSpy = spyOn(customResourcesService, 'getCorrespondingNodeIds').and + .callFake((id) => { + if (id === '-sites-') { + return of(['123456testId', '09876543testId']); + } + return of([id]); + }); + + component.currentFolderId = 'cat-girl-nuku-nuku'; + component.documentList.ngOnInit(); + + fixture.detectChanges(); + }); + + it('should the user query get updated when the user types in the search input', fakeAsync(() => { + const updateSpy = spyOn(searchQueryBuilderService, 'update'); + typeToSearchBox('search-term'); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(updateSpy).toHaveBeenCalled(); + expect(searchQueryBuilderService.userQuery).toEqual('(search-term*)'); + expect(component.searchTerm).toEqual('search-term'); + })); + + it('should perform a search when the queryBody gets updated and it is defined', fakeAsync(() => { + typeToSearchBox('search-term'); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); + })); + + it('should NOT perform a search and clear the results when the queryBody gets updated and it is NOT defined', async () => { + spyOn(component, 'clearSearch'); + + searchQueryBuilderService.userQuery = ''; + searchQueryBuilderService.update(); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(searchSpy).not.toHaveBeenCalled(); + expect(component.clearSearch).toHaveBeenCalled(); + }); + + it('should reset the search term when clicking the clear icon', async () => { + component.searchTerm = 'search-term'; + searchQueryBuilderService.userQuery = 'search-term'; + spyOn(component, 'clearSearch'); + + fixture.detectChanges(); + const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); + clearIcon.nativeElement.click(); + + fixture.detectChanges(); + + expect(searchQueryBuilderService.userQuery).toEqual(''); + expect(component.searchTerm).toEqual(''); + expect(component.clearSearch).toHaveBeenCalled(); + }); + + it('should load the results by calling the search api on search change', fakeAsync(() => { + typeToSearchBox('search-term'); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); + })); + + it('should the query include the show files filterQuery', fakeAsync(() => { + component.showFilesInResult = true; + typeToSearchBox('search-term'); + + const expectedQueryBody = mockQueryBody; + expectedQueryBody.filterQueries.push({ + query: `TYPE:'cm:folder' OR TYPE:'cm:content'` + }); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); + })); + + it('should reset the currently chosen node in case of starting a new search', fakeAsync(() => { + component.chosenNode = [entry]; + typeToSearchBox('kakarot'); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(component.chosenNode).toBeNull(); + })); + + it('should update the breadcrumb when changing to a custom site', async () => { + component.documentList.folderNode = { id: 'fakeNodeId', isFolder: true, path: {} } as Node; + + component.siteChanged({ entry: { guid: '-mysites-', title: 'My Sites' } } as SiteEntry); + + expect(component.breadcrumbFolderTitle).toBe('My Sites'); + }); + + it('should perform a search when selecting a site with the correct query', fakeAsync(() => { + typeToSearchBox('search-term'); + + tick(debounceSearch); + + expect(searchSpy.calls.count()).toBe(1, 'Search count should be one after only one search'); + + component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); + + const expectedQueryBody = mockQueryBody; + expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }]; + + expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change'); + expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); + })); + + it('should create the query with the right parameters on changing the site selectBox value from a custom dropdown menu', fakeAsync(() => { + component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; + component.documentList.folderNode = { id: 'fakeNodeId', isFolder: true, path: {} } as Node; + fixture.detectChanges(); + + typeToSearchBox('search-term'); + + tick(debounceSearch); + + expect(searchSpy.calls.count()).toBe(1); + + component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); + + const expectedQueryBodyWithSiteChange = mockQueryBody; + expectedQueryBodyWithSiteChange.filterQueries = [ + { query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'` } + ]; + + expect(searchSpy).toHaveBeenCalled(); + expect(searchSpy.calls.count()).toBe(2); + expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); + expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); + })); + + it('should get the corresponding node ids on search when a known alias is selected from dropdown', fakeAsync(() => { + component.documentList.folderNode = { id: 'fakeNodeId', isFolder: true, path: {} } as Node; + + typeToSearchBox('vegeta'); + + tick(debounceSearch); + + component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); + expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(1, 'getCorrespondingNodeIdsSpy calls count should be one after the site changes to known alias \'-sites\-'); + expect(getCorrespondingNodeIdsSpy.calls.mostRecent().args[0]).toEqual('-sites-'); + })); + + it('should get the corresponding node ids on search when a known alias is selected from CUSTOM dropdown', fakeAsync(() => { + component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; + component.documentList.folderNode = { id: 'fakeNodeId', isFolder: true, path: {} } as Node; + + fixture.detectChanges(); + + typeToSearchBox('vegeta'); + + tick(debounceSearch); + + component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); + expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(1); + expect(getCorrespondingNodeIdsSpy.calls.mostRecent().args[0]).toEqual('-sites-'); + })); + + it('should NOT get the corresponding node ids on search when NOTHING is selected from dropdown', fakeAsync(() => { + component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; + fixture.detectChanges(); + + typeToSearchBox('vegeta'); + + tick(debounceSearch); + + expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy calls count should be 0 when no site is selected'); + })); + + it('should NOT get the corresponding node ids on search when NO known alias is selected from dropdown', fakeAsync(() => { + typeToSearchBox('vegeta'); + tick(debounceSearch); + + expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy should not be called'); + + component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); + + expect(getCorrespondingNodeIdsSpy).not.toHaveBeenCalled(); + })); + + it('should NOT get the corresponding node ids on search when NO known alias is selected from CUSTOM dropdown', fakeAsync(() => { + component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; + fixture.detectChanges(); + + typeToSearchBox('vegeta'); + tick(debounceSearch); + + expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy should not be called'); + + component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); + + expect(getCorrespondingNodeIdsSpy).not.toHaveBeenCalled(); + })); + + it('should show the search icon by default without the X (clear) icon', fakeAsync(() => { + fixture.detectChanges(); + tick(debounceSearch); + + const searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]')); + const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); + + expect(searchIcon).not.toBeNull('Search icon should be in the DOM'); + expect(clearIcon).toBeNull('Clear icon should NOT be in the DOM'); + })); + + it('should show the X (clear) icon without the search icon when the search contains at least one character', fakeAsync(() => { + fixture.detectChanges(); + typeToSearchBox('123'); + tick(debounceSearch); + + fixture.detectChanges(); + + const searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]')); + const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); + + expect(searchIcon).toBeNull('Search icon should NOT be in the DOM'); + expect(clearIcon).not.toBeNull('Clear icon should be in the DOM'); + })); + + it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', async () => { + component.chosenNode = [entry]; + + component.nodePaging = { + list: { + entries: [{ entry }] + } + }; + component.searchTerm = 'piccolo'; + component.showingSearchResults = true; + + component.clear(); + + expect(component.searchTerm).toBe(''); + expect(component.nodePaging).toEqual(null); + expect(component.chosenNode).toBeNull(); + expect(component.showingSearchResults).toBeFalsy(); + }); + + it('should the query restrict the search to the currentFolderId in case is defined', fakeAsync(() => { + component.currentFolderId = 'my-root-id'; + component.restrictRootToCurrentFolderId = true; + component.ngOnInit(); + typeToSearchBox('search-term'); + tick(debounceSearch); + + const expectedQueryBody = mockQueryBody; + expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-root-id'` }]; + + expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); + })); + + it('should emit showingSearch event with true while searching', async () => { + searchQueryBuilderService.userQuery = 'mock-search-term'; + searchQueryBuilderService.update(); + spyOn(customResourcesService, 'hasCorrespondingNodeIds').and.returnValue(true); + const showingSearchSpy = spyOn(component.showingSearch, 'emit'); + + component.queryBuilderService.execute({ query: { query: 'search' } }); + + triggerSearchResults(fakeResultSetPaging); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.showingSearchResults).toBe(true); + expect(showingSearchSpy).toHaveBeenCalledWith(true); + }); + + it('should emit showingSearch event with false if you remove search term without clicking on X (icon) icon', fakeAsync(() => { + const showingSearchSpy = spyOn(component.showingSearch, 'emit'); + typeToSearchBox(''); + tick(debounceSearch); + + fixture.detectChanges(); + + expect(component.showingSearchResults).toBe(false); + expect(showingSearchSpy).toHaveBeenCalledWith(false); + })); + + it('should emit showingResults event with false when clicking on the X (clear) icon', async () => { + const showingSearchSpy = spyOn(component.showingSearch, 'emit'); + component.chosenNode = [entry]; + + component.nodePaging = { + list: { + entries: [{ entry }] + } + }; + component.searchTerm = 'piccolo'; + component.showingSearchResults = true; + + component.clear(); + + expect(component.showingSearchResults).toBe(false); + expect(showingSearchSpy).toHaveBeenCalledWith(false); + }); + + it('should emit showingResults event with false if search api fails', async () => { + searchQueryBuilderService.userQuery = 'mock-search-term'; + searchQueryBuilderService.update(); + getCorrespondingNodeIdsSpy.and.throwError('Failed'); + const showingSearchSpy = spyOn(component.showingSearch, 'emit'); + component.queryBuilderService.execute({ query: { query: 'search' } }); + + triggerSearchResults(fakeResultSetPaging); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.showingSearchResults).toBe(true); + expect(showingSearchSpy).toHaveBeenCalledWith(true); + }); + + it('should the query restrict the search to the site and not to the currentFolderId in case is changed', async () => { + component.queryBuilderService.userQuery = 'search-term*'; + component.currentFolderId = 'my-root-id'; + component.restrictRootToCurrentFolderId = true; + component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry); + + const expectedQueryBodyWithSiteChange = mockQueryBody; + expectedQueryBodyWithSiteChange.filterQueries = [ + { query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` } + ]; + + expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); + }); + + it('should restrict the breadcrumb to the currentFolderId in case restrictedRoot is true', async () => { + component.currentFolderId = 'my-root-id'; + component.restrictRootToCurrentFolderId = true; + component.ngOnInit(); + expect(component.breadcrumbRootId).toEqual('my-root-id'); + }); + + it('should NOT restrict the breadcrumb to the currentFolderId in case restrictedRoot is false', async () => { + component.currentFolderId = 'my-root-id'; + component.restrictRootToCurrentFolderId = false; + component.ngOnInit(); + expect(component.breadcrumbRootId).toBeUndefined(); + }); + + it('should clear the search field, nodes and chosenNode when deleting the search input', fakeAsync(() => { + spyOn(component, 'clearSearch').and.callThrough(); + typeToSearchBox('a'); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(searchSpy.calls.count()).toBe(1); + + typeToSearchBox(''); + + tick(debounceSearch); + fixture.detectChanges(); + + expect(searchSpy.calls.count()).toBe(1, 'no other search has been performed'); + expect(component.clearSearch).toHaveBeenCalled(); + expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku', 'back to the folder in which the search was performed'); + })); + + it('should folderIdToShow equal the folder node id when navigation changes', async () => { + component.folderIdToShow = null; + const folderChangeEvent: NodeEntryEvent = new NodeEntryEvent(fakeNodeEntry); + component.onFolderChange(folderChangeEvent); + + expect(component.folderIdToShow).toEqual(fakeNodeEntry.id); + }); + + it('should clear the search field, nodes and chosenNode on folder navigation in the results list', async () => { + spyOn(component, 'clearSearch').and.callThrough(); + triggerSearchResults(fakeResultSetPaging); + + fixture.detectChanges(); + + component.onFolderChange(nodeEntryEvent); + fixture.detectChanges(); + + expect(component.clearSearch).toHaveBeenCalled(); + }); + + it('should show nodes from the same folder as selected in the dropdown on clearing the search input', fakeAsync(() => { + typeToSearchBox('piccolo'); + tick(debounceSearch); + + expect(searchSpy.calls.count()).toBe(1); + + component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); + + expect(searchSpy.calls.count()).toBe(2); + + component.clear(); + + expect(component.searchTerm).toBe(''); + expect(component.folderIdToShow).toBe('namek'); + })); + + it('should show the current folder\'s content instead of search results if search was not performed', async () => { + const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); + }); + + it('should pass through the rowFilter to the documentList', async () => { + const filter = (shareDataRow: ShareDataRow) => + shareDataRow.node.entry.name === 'impossible-name'; + + component.rowFilter = filter; + + fixture.detectChanges(); + + const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.rowFilter({ + node: { + entry: new Node({ + name: 'impossible-name', + id: 'name' + }) + } + })) + .toBe(filter({ + node: { + entry: new Node({ + name: 'impossible-name', + id: 'name' + }) + } + } as ShareDataRow)); + }); + + it('should pass through the excludeSiteContent to the rowFilter of the documentList', async () => { + component.excludeSiteContent = ['blog']; + + fixture.detectChanges(); + + const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.rowFilter).toBeTruthy('Document list should have had a rowFilter'); + + const testSiteContent = new Node({ id: 'blog-id', properties: { 'st:componentId': 'blog' } }); + expect(documentList.componentInstance.rowFilter({ node: { entry: testSiteContent } }, null, null)) + .toBe(false); + }); + + it('should pass through the imageResolver to the documentList', async () => { + const resolver = () => 'piccolo'; + component.imageResolver = resolver; + + fixture.detectChanges(); + + const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.imageResolver).toBe(resolver); + }); + + it('should show the result list when search was performed', (done) => { + typeToSearchBox(); + + setTimeout(() => { + triggerSearchResults(fakeResultSetPaging); + + fixture.detectChanges(); + const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(component.hasValidQuery).toEqual(true); + expect(documentList.componentInstance.currentFolderId).toBeNull(); + done(); + }, 300); + }); + + it('should not show the result list when results are returned but there is no search term typed', (done) => { + searchQueryBuilderService.userQuery = ''; + searchQueryBuilderService.update(); + + setTimeout(() => { + triggerSearchResults(fakeResultSetPaging); + fixture.detectChanges(); + + expect(component.hasValidQuery).toEqual(false); + expect(component.showingSearchResults).toEqual(false); + done(); + }, 300); + }); + + it('should highlight the results when search was performed in the next timeframe', (done) => { + typeToSearchBox('My'); + + setTimeout(() => { + triggerSearchResults(fakeResultSetPaging); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(fixture.debugElement.nativeElement.querySelector('.adf-highlight').innerHTML).toBe('My'); + + done(); + }); + }, 300); + }); + + it('should show the default text instead of result list if search was cleared', (done) => { + typeToSearchBox(); + + setTimeout(() => { + triggerSearchResults(fakeResultSetPaging); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const clearButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); + expect(clearButton).not.toBeNull('Clear button should be in DOM'); + clearButton.triggerEventHandler('click', {}); + fixture.detectChanges(); + + const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); + done(); + }); + }, 300); + }); + + it('should reload the original folderId when clearing the search input', fakeAsync(() => { + typeToSearchBox('search-term'); + tick(debounceSearch); + fixture.detectChanges(); + + expect(component.folderIdToShow).toBe(null); + + typeToSearchBox(''); + tick(debounceSearch); + fixture.detectChanges(); + + expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku'); + })); + + it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => { + component.siteChanged({ entry: { guid: 'Kame-Sennin Muten Roshi' } } as SiteEntry); + fixture.detectChanges(); + + let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); + expect(documentList.componentInstance.currentFolderId).toBe('Kame-Sennin Muten Roshi'); + + component.siteChanged({ entry: { guid: undefined } } as SiteEntry); + fixture.detectChanges(); + + documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); + expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); + + done(); + }); + + describe('Pagination "Load more" button', () => { + + it('should NOT be shown by default', () => { + fixture.detectChanges(); + const pagination = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]')); + expect(pagination).toBeNull(); + }); + + it('button callback should load the next batch of folder results when there is no searchTerm', () => { + component.searchTerm = ''; + fixture.detectChanges(); + + component.getNextPageOfSearch({ + hasMoreItems: false, + skipCount: 10, + maxItems: 45, + totalItems: 0 + }); + + fixture.detectChanges(); + expect(component.searchTerm).toBe(''); + + expect(component.infiniteScroll).toBeTruthy(); + expect(component.queryBuilderService.paging.maxItems).toBe(45); + expect(searchSpy).not.toHaveBeenCalled(); + }); + + it('should set its loading state to true to perform a new search', async () => { + component.prepareDialogForNewSearch(mockQueryBody); + fixture.detectChanges(); + await fixture.whenStable(); + + const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]'); + const paginationLoading = fixture.debugElement.query(spinnerSelector); + + expect(paginationLoading).not.toBeNull(); + }); + + it('Should infinite pagination target be null when we use it for search ', fakeAsync(() => { + component.showingSearchResults = true; + typeToSearchBox('shenron'); + tick(debounceSearch); + fixture.detectChanges(); + + expect(component.target).toBeNull(); + })); + + it('Should infinite pagination target be present when search finish', () => { + triggerSearchResults(fakeResultSetPaging); + fixture.detectChanges(); + + expect(component.target).not.toBeNull(); + }); + + it('Should infinite pagination target on init be the document list', fakeAsync(() => { + component.showingSearchResults = true; + + expect(component.target).toEqual(component.documentList); + })); + + it('Should set the scope to nodes when the component inits', () => { + const expectedScope: RequestScope = { locations: 'nodes' }; + const setScopeSpy = spyOn(component.queryBuilderService, 'setScope'); + component.ngOnInit(); + + expect(setScopeSpy).toHaveBeenCalledWith(expectedScope); + }); + }); + }); + + }); +}); diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.spec.ts index 71849fdde0..c598978799 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.spec.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.spec.ts @@ -18,20 +18,39 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { MinimalNode, Node, NodeEntry, NodePaging, RequestScope, ResultSetPaging, SiteEntry, SitePaging, UserInfo } from '@alfresco/js-api'; -import { AppConfigService, FileModel, FileUploadStatus, NodesApiService, setupTestBed, SitesService, UploadService, FileUploadCompleteEvent, DataRow, ThumbnailService, ContentService, DataColumn } from '@alfresco/adf-core'; +import { + MinimalNode, + Node, + NodeEntry, + NodePaging, + ResultSetPaging, + SiteEntry, + SitePaging, + UserInfo +} from '@alfresco/js-api'; +import { + AppConfigService, + FileModel, + FileUploadStatus, + NodesApiService, + setupTestBed, + SitesService, + UploadService, + FileUploadCompleteEvent, + DataRow, + ThumbnailService, + ContentService, + DataColumn +} from '@alfresco/adf-core'; import { of, throwError } from 'rxjs'; import { DropdownBreadcrumbComponent } from '../breadcrumb'; import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component'; import { ContentTestingModule } from '../testing/content.testing.module'; import { DocumentListService } from '../document-list/services/document-list.service'; -import { DocumentListComponent } from '../document-list/components/document-list.component'; import { DropdownSitesComponent } from '../site-dropdown/sites-dropdown.component'; -import { CustomResourcesService } from '../document-list/services/custom-resources.service'; import { NodeEntryEvent, ShareDataRow, ShareDataTableAdapter } from '../document-list'; import { TranslateModule } from '@ngx-translate/core'; import { SearchQueryBuilderService } from '../search'; -import { mockQueryBody } from '../mock/search-query.mock'; import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service'; import { mockContentModelTextProperty } from '../mock/content-model.mock'; @@ -55,12 +74,10 @@ const fakeResultSetPaging: ResultSetPaging = { }; describe('ContentNodeSelectorPanelComponent', () => { - const debounceSearch = 200; let component: ContentNodeSelectorPanelComponent; let fixture: ComponentFixture; let nodeService: NodesApiService; let sitesService: SitesService; - let searchSpy: jasmine.Spy; const fakeNodeEntry = new Node({ id: 'fakeId' }); const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry); let searchQueryBuilderService: SearchQueryBuilderService; @@ -69,13 +86,6 @@ describe('ContentNodeSelectorPanelComponent', () => { let thumbnailService: ThumbnailService; let contentService: ContentService; - const typeToSearchBox = (searchTerm = 'string-to-search') => { - const searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]')); - searchInput.nativeElement.value = searchTerm; - component.searchInput.setValue(searchTerm); - fixture.detectChanges(); - }; - const triggerSearchResults = (searchResults: ResultSetPaging) => { component.queryBuilderService.executed.next(searchResults); }; @@ -105,9 +115,18 @@ describe('ContentNodeSelectorPanelComponent', () => { searchQueryBuilderService = component.queryBuilderService; component.queryBuilderService.resetToDefaults(); - spyOn(nodeService, 'getNode').and.returnValue(of(new MinimalNode({ id: 'fake-node', path: { elements: [{ nodeType: 'st:site', name: 'fake-site'}] } }))); - searchSpy = spyOn(searchQueryBuilderService, 'execute'); - const fakeSite = new SiteEntry({ entry: { id: 'fake-site', guid: 'fake-site', title: 'fake-site', visibility: 'visible' } }); + spyOn(nodeService, 'getNode').and.returnValue(of(new MinimalNode({ + id: 'fake-node', + path: { elements: [{ nodeType: 'st:site', name: 'fake-site' }] } + }))); + const fakeSite = new SiteEntry({ + entry: { + id: 'fake-site', + guid: 'fake-site', + title: 'fake-site', + visibility: 'visible' + } + }); spyOn(sitesService, 'getSite').and.returnValue(of(fakeSite)); }); @@ -115,6 +134,40 @@ describe('ContentNodeSelectorPanelComponent', () => { fixture.destroy(); }); + describe('Site selection', () => { + + beforeEach(() => { + spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } }))); + component.currentFolderId = 'fake-starting-folder'; + }); + + it('should trigger siteChange event on init with parent site Title of start folder', (done) => { + component.siteChange.subscribe((siteTitle: string) => { + expect(siteTitle).toBe('fake-site'); + done(); + }); + + component.ngOnInit(); + fixture.detectChanges(); + expect(component.startSiteGuid).toBe('fake-site'); + }); + + it('should trigger siteChange event when a site is selected in sites-dropdown', (done) => { + const fakeSiteEntry = new SiteEntry({ entry: { title: 'fake-new-site', guid: 'fake-new-site' } }); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + component.siteChange.subscribe((siteTitle: string) => { + expect(siteTitle).toBe('fake-new-site'); + done(); + }); + + const sitesDropdown = fixture.debugElement.query(By.directive(DropdownSitesComponent)); + sitesDropdown.componentInstance.selectedSite({ value: fakeSiteEntry }); + }); + }); + }); + describe('Parameters', () => { let documentListService: DocumentListService; @@ -122,7 +175,7 @@ describe('ContentNodeSelectorPanelComponent', () => { beforeEach(() => { documentListService = TestBed.inject(DocumentListService); - spyOn(documentListService, 'getFolderNode').and.returnValue(of({ entry: { path: { elements: [] } } })); + spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry())); spyOn(documentListService, 'getFolder').and.returnValue(throwError('No results for test')); spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { @@ -142,7 +195,7 @@ describe('ContentNodeSelectorPanelComponent', () => { }); it('should trigger the select event when selection has been made', (done) => { - const expectedNode = { id: 'fakeid'} as Node; + const expectedNode = { id: 'fakeid' } as Node; component.select.subscribe((nodes) => { expect(nodes.length).toBe(1); expect(nodes[0]).toBe(expectedNode); @@ -196,27 +249,27 @@ describe('ContentNodeSelectorPanelComponent', () => { it('should render search input by default', () => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement.querySelector('.adf-content-node-selector-content-input')) - .not.toBe(null); + .not.toBe(null); }); it('should not render search input if `showSearch` is false', () => { component.showSearch = false; fixture.detectChanges(); expect(fixture.debugElement.nativeElement.querySelector('.adf-content-node-selector-content-input')) - .toBe(null); + .toBe(null); }); it('should render sites list dropdown by default', () => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement.querySelector('adf-sites-dropdown')) - .not.toBe(null); + .not.toBe(null); }); it('should not render sites list dropdown if `showDropdownSiteList` is false', () => { component.showDropdownSiteList = false; fixture.detectChanges(); expect(fixture.debugElement.nativeElement.querySelector('adf-sites-dropdown')) - .toBe(null); + .toBe(null); }); }); @@ -227,7 +280,7 @@ describe('ContentNodeSelectorPanelComponent', () => { beforeEach(() => { documentListService = TestBed.inject(DocumentListService); - spyOn(documentListService, 'getFolderNode').and.returnValue(of({ entry: { path: { elements: [] } } })); + spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry())); spyOn(documentListService, 'getFolder').and.returnValue(throwError('No results for test')); spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } }))); @@ -275,7 +328,7 @@ describe('ContentNodeSelectorPanelComponent', () => { triggerSearchResults(fakeResultSetPaging); const chosenNode = new Node({ path: { elements: ['one'] } }); - component.onCurrentSelection([ { entry: chosenNode } ]); + component.onCurrentSelection([{ entry: chosenNode }]); fixture.detectChanges(); const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent)); @@ -306,7 +359,7 @@ describe('ContentNodeSelectorPanelComponent', () => { fixture.detectChanges(); const chosenNode = { path: { elements: [] } } as Node; - component.onCurrentSelection([ { entry: chosenNode } ]); + component.onCurrentSelection([{ entry: chosenNode }]); fixture.detectChanges(); const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent)); @@ -348,692 +401,9 @@ describe('ContentNodeSelectorPanelComponent', () => { }); }); - describe('Site selection', () => { - - beforeEach(() => { - spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } }))); - component.currentFolderId = 'fake-starting-folder'; - }); - - it('should trigger siteChange event on init with parent site Title of start folder', (done) => { - component.siteChange.subscribe((siteTitle: string) => { - expect(siteTitle).toBe('fake-site'); - done(); - }); - - component.ngOnInit(); - fixture.detectChanges(); - expect(component.startSiteGuid).toBe('fake-site'); - }); - - it('should trigger siteChange event when a site is selected in sites-dropdown', (done) => { - const fakeSiteEntry = new SiteEntry({ entry: { title: 'fake-new-site', guid: 'fake-new-site' } }); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - component.siteChange.subscribe((siteTitle: string) => { - expect(siteTitle).toBe('fake-new-site'); - done(); - }); - - const sitesDropdown = fixture.debugElement.query(By.directive(DropdownSitesComponent)); - sitesDropdown.componentInstance.selectedSite({value: fakeSiteEntry}); - }); - }); - }); - - describe('Search functionality', () => { - let getCorrespondingNodeIdsSpy; - let customResourcesService: CustomResourcesService; - const entry: Node = { id: 'fakeid'} as Node; - - beforeEach(() => { - const documentListService = TestBed.inject(DocumentListService); - const expectedDefaultFolderNode = { entry: { path: { elements: [] } } }; - component.isSelectionValid = (node: Node) => node.isFile; - - spyOn(documentListService, 'getFolderNode').and.returnValue(of(expectedDefaultFolderNode)); - spyOn(documentListService, 'getFolder').and.returnValue(of(new NodePaging({ - list: { - pagination: {}, - entries: [], - source: {} - } - }))); - - spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } }))); - - customResourcesService = TestBed.inject(CustomResourcesService); - getCorrespondingNodeIdsSpy = spyOn(customResourcesService, 'getCorrespondingNodeIds').and - .callFake((id) => { - if (id === '-sites-') { - return of(['123456testId', '09876543testId']); - } - return of([id]); - }); - - component.currentFolderId = 'cat-girl-nuku-nuku'; - component.documentList.ngOnInit(); - - fixture.detectChanges(); - }); - - it('should the user query get updated when the user types in the search input', fakeAsync(() => { - const updateSpy = spyOn(searchQueryBuilderService, 'update'); - typeToSearchBox('search-term'); - - tick(debounceSearch); - fixture.detectChanges(); - - expect(updateSpy).toHaveBeenCalled(); - expect(searchQueryBuilderService.userQuery).toEqual('(search-term*)'); - expect(component.searchTerm).toEqual('search-term'); - })); - - it('should perform a search when the queryBody gets updated and it is defined', async () => { - searchQueryBuilderService.userQuery = 'search-term*'; - searchQueryBuilderService.update(); - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); - }); - - it('should NOT perform a search and clear the results when the queryBody gets updated and it is NOT defined', async () => { - spyOn(component, 'clearSearch'); - - searchQueryBuilderService.userQuery = ''; - searchQueryBuilderService.update(); - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(searchSpy).not.toHaveBeenCalled(); - expect(component.clearSearch).toHaveBeenCalled(); - }); - - it('should reset the search term when clicking the clear icon', () => { - component.searchTerm = 'search-term'; - searchQueryBuilderService.userQuery = 'search-term'; - spyOn(component, 'clearSearch'); - - fixture.detectChanges(); - const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); - clearIcon.nativeElement.click(); - - fixture.detectChanges(); - - expect(searchQueryBuilderService.userQuery).toEqual(''); - expect(component.searchTerm).toEqual(''); - expect(component.clearSearch).toHaveBeenCalled(); - }); - - it('should load the results by calling the search api on search change', fakeAsync(() => { - typeToSearchBox('search-term'); - - tick(debounceSearch); - fixture.detectChanges(); - - expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); - })); - - it('should the query include the show files filterQuery', fakeAsync(() => { - component.showFilesInResult = true; - typeToSearchBox('search-term'); - - const expectedQueryBody = mockQueryBody; - expectedQueryBody.filterQueries.push({ - query: `TYPE:'cm:folder' OR TYPE:'cm:content'` - }); - - tick(debounceSearch); - fixture.detectChanges(); - - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); - })); - - it('should reset the currently chosen node in case of starting a new search', fakeAsync(() => { - component.chosenNode = [entry]; - typeToSearchBox('kakarot'); - - tick(debounceSearch); - fixture.detectChanges(); - - expect(component.chosenNode).toBeNull(); - })); - - it('should update the breadcrumb when changing to a custom site', async () => { - component.siteChanged({ entry: { guid: '-mysites-', title: 'My Sites' } } as SiteEntry); - - expect(component.breadcrumbFolderTitle).toBe('My Sites'); - }); - - it('should perform a search when selecting a site with the correct query', fakeAsync(() => { - typeToSearchBox('search-term'); - - tick(debounceSearch); - - expect(searchSpy.calls.count()).toBe(1, 'Search count should be one after only one search'); - - component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); - - const expectedQueryBody = mockQueryBody; - expectedQueryBody.filterQueries = [ { query: `ANCESTOR:'workspace://SpacesStore/namek'`} ]; - - expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change'); - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); - })); - - it('should create the query with the right parameters on changing the site selectBox value from a custom dropdown menu', fakeAsync(() => { - component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; - fixture.detectChanges(); - - typeToSearchBox('search-term'); - - tick(debounceSearch); - - expect(searchSpy.calls.count()).toBe(1); - - component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); - - const expectedQueryBodyWithSiteChange = mockQueryBody; - expectedQueryBodyWithSiteChange.filterQueries = [ - { query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'` } - ]; - - expect(searchSpy).toHaveBeenCalled(); - expect(searchSpy.calls.count()).toBe(2); - expect(searchSpy).toHaveBeenCalledWith(mockQueryBody); - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); - })); - - it('should get the corresponding node ids on search when a known alias is selected from dropdown', fakeAsync(() => { - typeToSearchBox('vegeta'); - - tick(debounceSearch); - - component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); - expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(1, 'getCorrespondingNodeIdsSpy calls count should be one after the site changes to known alias \'-sites\-'); - expect(getCorrespondingNodeIdsSpy.calls.mostRecent().args[0]).toEqual('-sites-'); - })); - - it('should get the corresponding node ids on search when a known alias is selected from CUSTOM dropdown', fakeAsync(() => { - component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; - fixture.detectChanges(); - - typeToSearchBox('vegeta'); - - tick(debounceSearch); - - component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry); - expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(1); - expect(getCorrespondingNodeIdsSpy.calls.mostRecent().args[0]).toEqual('-sites-'); - })); - - it('should NOT get the corresponding node ids on search when NOTHING is selected from dropdown', fakeAsync(() => { - component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; - fixture.detectChanges(); - - typeToSearchBox('vegeta'); - - tick(debounceSearch); - - expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy calls count should be 0 when no site is selected'); - })); - - it('should NOT get the corresponding node ids on search when NO known alias is selected from dropdown', fakeAsync(() => { - typeToSearchBox('vegeta'); - tick(debounceSearch); - - expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy should not be called'); - - component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); - - expect(getCorrespondingNodeIdsSpy).not.toHaveBeenCalled(); - })); - - it('should NOT get the corresponding node ids on search when NO known alias is selected from CUSTOM dropdown', fakeAsync(() => { - component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging; - fixture.detectChanges(); - - typeToSearchBox('vegeta'); - tick(debounceSearch); - - expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy should not be called'); - - component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); - - expect(getCorrespondingNodeIdsSpy).not.toHaveBeenCalled(); - })); - - it('should show the search icon by default without the X (clear) icon', fakeAsync(() => { - fixture.detectChanges(); - tick(debounceSearch); - - const searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]')); - const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); - - expect(searchIcon).not.toBeNull('Search icon should be in the DOM'); - expect(clearIcon).toBeNull('Clear icon should NOT be in the DOM'); - })); - - it('should show the X (clear) icon without the search icon when the search contains at least one character', fakeAsync(() => { - fixture.detectChanges(); - typeToSearchBox('123'); - tick(debounceSearch); - - fixture.detectChanges(); - - const searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]')); - const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); - - expect(searchIcon).toBeNull('Search icon should NOT be in the DOM'); - expect(clearIcon).not.toBeNull('Clear icon should be in the DOM'); - })); - - it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => { - component.chosenNode = [entry]; - - component.nodePaging = { - list: { - entries: [{ entry }] - } - }; - component.searchTerm = 'piccolo'; - component.showingSearchResults = true; - - component.clear(); - - expect(component.searchTerm).toBe(''); - expect(component.nodePaging).toEqual(null); - expect(component.chosenNode).toBeNull(); - expect(component.showingSearchResults).toBeFalsy(); - }); - - it('should the query restrict the search to the currentFolderId in case is defined', fakeAsync(() => { - component.currentFolderId = 'my-root-id'; - component.restrictRootToCurrentFolderId = true; - component.ngOnInit(); - typeToSearchBox('search-term'); - tick(debounceSearch); - - const expectedQueryBody = mockQueryBody; - expectedQueryBody.filterQueries = [ { query: `ANCESTOR:'workspace://SpacesStore/my-root-id'`} ]; - - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody); - })); - - it('should emit showingSearch event with true while searching', async () => { - searchQueryBuilderService.userQuery = 'mock-search-term'; - searchQueryBuilderService.update(); - spyOn(customResourcesService, 'hasCorrespondingNodeIds').and.returnValue(true); - const showingSearchSpy = spyOn(component.showingSearch, 'emit'); - - component.queryBuilderService.execute({ query: { query: 'search' } }); - - triggerSearchResults(fakeResultSetPaging); - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.showingSearchResults).toBe(true); - expect(showingSearchSpy).toHaveBeenCalledWith(true); - }); - - it('should emit showingSearch event with false if you remove search term without clicking on X (icon) icon', fakeAsync(() => { - const showingSearchSpy = spyOn(component.showingSearch, 'emit'); - typeToSearchBox(''); - tick(debounceSearch); - - fixture.detectChanges(); - - expect(component.showingSearchResults).toBe(false); - expect(showingSearchSpy).toHaveBeenCalledWith(false); - })); - - it('should emit showingResults event with false when clicking on the X (clear) icon', () => { - const showingSearchSpy = spyOn(component.showingSearch, 'emit'); - component.chosenNode = [entry]; - - component.nodePaging = { - list: { - entries: [{ entry }] - } - }; - component.searchTerm = 'piccolo'; - component.showingSearchResults = true; - - component.clear(); - - expect(component.showingSearchResults).toBe(false); - expect(showingSearchSpy).toHaveBeenCalledWith(false); - }); - - it('should emit showingResults event with false if search api fails', async () => { - searchQueryBuilderService.userQuery = 'mock-search-term'; - searchQueryBuilderService.update(); - getCorrespondingNodeIdsSpy.and.throwError('Failed'); - const showingSearchSpy = spyOn(component.showingSearch, 'emit'); - component.queryBuilderService.execute({ query: { query: 'search' } }); - - triggerSearchResults(fakeResultSetPaging); - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.showingSearchResults).toBe(true); - expect(showingSearchSpy).toHaveBeenCalledWith(true); - }); - - it('should the query restrict the search to the site and not to the currentFolderId in case is changed', () => { - component.queryBuilderService.userQuery = 'search-term*'; - component.currentFolderId = 'my-root-id'; - component.restrictRootToCurrentFolderId = true; - component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry); - - const expectedQueryBodyWithSiteChange = mockQueryBody; - expectedQueryBodyWithSiteChange.filterQueries = [ - { query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` } - ]; - - expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange); - }); - - it('should restrict the breadcrumb to the currentFolderId in case restrictedRoot is true', () => { - component.currentFolderId = 'my-root-id'; - component.restrictRootToCurrentFolderId = true; - component.ngOnInit(); - expect(component.breadcrumbRootId).toEqual('my-root-id'); - }); - - it('should NOT restrict the breadcrumb to the currentFolderId in case restrictedRoot is false', () => { - component.currentFolderId = 'my-root-id'; - component.restrictRootToCurrentFolderId = false; - component.ngOnInit(); - expect(component.breadcrumbRootId).toBeUndefined(); - }); - - it('should clear the search field, nodes and chosenNode when deleting the search input', fakeAsync (() => { - spyOn(component, 'clearSearch').and.callThrough(); - typeToSearchBox('a'); - - tick(debounceSearch); - fixture.detectChanges(); - - expect(searchSpy.calls.count()).toBe(1); - - typeToSearchBox(''); - - tick(debounceSearch); - fixture.detectChanges(); - - expect(searchSpy.calls.count()).toBe(1, 'no other search has been performed'); - expect(component.clearSearch).toHaveBeenCalled(); - expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku', 'back to the folder in which the search was performed'); - })); - - it('should folderIdToShow equal the folder node id when navigation changes', () => { - component.folderIdToShow = null; - const folderChangeEvent: NodeEntryEvent = new NodeEntryEvent(fakeNodeEntry); - component.onFolderChange(folderChangeEvent); - - expect(component.folderIdToShow).toEqual(fakeNodeEntry.id); - }); - - it('should clear the search field, nodes and chosenNode on folder navigation in the results list', async () => { - spyOn(component, 'clearSearch').and.callThrough(); - triggerSearchResults(fakeResultSetPaging); - - fixture.detectChanges(); - - component.onFolderChange(nodeEntryEvent); - fixture.detectChanges(); - - expect(component.clearSearch).toHaveBeenCalled(); - }); - - it('should show nodes from the same folder as selected in the dropdown on clearing the search input', fakeAsync (() => { - typeToSearchBox('piccolo'); - tick(debounceSearch); - - expect(searchSpy.calls.count()).toBe(1); - - component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry); - - expect(searchSpy.calls.count()).toBe(2); - - component.clear(); - - expect(component.searchTerm).toBe(''); - expect(component.folderIdToShow).toBe('namek'); - })); - - it('should show the current folder\'s content instead of search results if search was not performed', () => { - const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); - }); - - it('should pass through the rowFilter to the documentList', () => { - const filter = (shareDataRow: ShareDataRow) => - shareDataRow.node.entry.name === 'impossible-name'; - - component.rowFilter = filter; - - fixture.detectChanges(); - - const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.rowFilter({ - node: { - entry: new Node({ - name: 'impossible-name', - id: 'name' - }) - } - })) - .toBe(filter({ - node: { - entry: new Node({ - name: 'impossible-name', - id: 'name' - }) - } - } as ShareDataRow)); - }); - - it('should pass through the excludeSiteContent to the rowFilter of the documentList', () => { - component.excludeSiteContent = ['blog']; - - fixture.detectChanges(); - - const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.rowFilter).toBeTruthy('Document list should have had a rowFilter'); - - const testSiteContent = new Node({ id: 'blog-id', properties: { 'st:componentId': 'blog' } }); - expect(documentList.componentInstance.rowFilter({ node: { entry: testSiteContent } }, null, null)) - .toBe(false); - }); - - it('should pass through the imageResolver to the documentList', () => { - const resolver = () => 'piccolo'; - component.imageResolver = resolver; - - fixture.detectChanges(); - - const documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.imageResolver).toBe(resolver); - }); - - it('should show the result list when search was performed', (done) => { - typeToSearchBox(); - - setTimeout(() => { - triggerSearchResults(fakeResultSetPaging); - - fixture.detectChanges(); - const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(component.hasValidQuery).toEqual(true); - expect(documentList.componentInstance.currentFolderId).toBeNull(); - done(); - }, 300); - }); - - it('should not show the result list when results are returned but there is no search term typed', (done) => { - searchQueryBuilderService.userQuery = ''; - searchQueryBuilderService.update(); - - setTimeout(() => { - triggerSearchResults(fakeResultSetPaging); - fixture.detectChanges(); - - expect(component.hasValidQuery).toEqual(false); - expect(component.showingSearchResults).toEqual(false); - done(); - }, 300); - }); - - it('should highlight the results when search was performed in the next timeframe', (done) => { - typeToSearchBox('My'); - - setTimeout(() => { - triggerSearchResults(fakeResultSetPaging); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(fixture.debugElement.nativeElement.querySelector('.adf-highlight').innerHTML).toBe('My'); - - done(); - }); - }, 300); - }); - - it('should show the default text instead of result list if search was cleared', (done) => { - typeToSearchBox(); - - setTimeout(() => { - triggerSearchResults(fakeResultSetPaging); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - const clearButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]')); - expect(clearButton).not.toBeNull('Clear button should be in DOM'); - clearButton.triggerEventHandler('click', {}); - fixture.detectChanges(); - - const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); - expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); - done(); - }); - }, 300); - }); - - it('should reload the original folderId when clearing the search input', fakeAsync(() => { - typeToSearchBox('search-term'); - tick(debounceSearch); - fixture.detectChanges(); - - expect(component.folderIdToShow).toBe(null); - - typeToSearchBox(''); - tick(debounceSearch); - fixture.detectChanges(); - - expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku'); - })); - - it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => { - component.siteChanged({ entry: { guid: 'Kame-Sennin Muten Roshi' } } as SiteEntry); - fixture.detectChanges(); - - let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); - expect(documentList.componentInstance.currentFolderId).toBe('Kame-Sennin Muten Roshi'); - - component.siteChanged({ entry: { guid: undefined } } as SiteEntry); - fixture.detectChanges(); - - documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); - expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku'); - - done(); - }); - - describe('Pagination "Load more" button', () => { - - it('should NOT be shown by default', () => { - fixture.detectChanges(); - const pagination = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]')); - expect(pagination).toBeNull(); - }); - - it('button callback should load the next batch of folder results when there is no searchTerm', () => { - component.searchTerm = ''; - fixture.detectChanges(); - - component.getNextPageOfSearch({ - hasMoreItems: false, - skipCount: 10, - maxItems: 45, - totalItems: 0 - }); - - fixture.detectChanges(); - expect(component.searchTerm).toBe(''); - - expect(component.infiniteScroll).toBeTruthy(); - expect(component.queryBuilderService.paging.maxItems).toBe(45); - expect(searchSpy).not.toHaveBeenCalled(); - }); - - it('should set its loading state to true to perform a new search', async () => { - component.prepareDialogForNewSearch(mockQueryBody); - fixture.detectChanges(); - await fixture.whenStable(); - - const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]'); - const paginationLoading = fixture.debugElement.query(spinnerSelector); - - expect(paginationLoading).not.toBeNull(); - }); - - it('Should infinite pagination target be null when we use it for search ', fakeAsync (() => { - component.showingSearchResults = true; - typeToSearchBox('shenron'); - tick(debounceSearch); - fixture.detectChanges(); - - expect(component.target).toBeNull(); - })); - - it('Should infinite pagination target be present when search finish', () => { - triggerSearchResults(fakeResultSetPaging); - fixture.detectChanges(); - - expect(component.target).not.toBeNull(); - }); - - it('Should infinite pagination target on init be the document list', fakeAsync(() => { - component.showingSearchResults = true; - - expect(component.target).toEqual(component.documentList); - })); - - it('Should set the scope to nodes when the component inits', () => { - const expectedScope: RequestScope = { locations: 'nodes' }; - const setScopeSpy = spyOn(component.queryBuilderService, 'setScope'); - component.ngOnInit(); - - expect(setScopeSpy).toHaveBeenCalledWith(expectedScope); - }); - }); - }); - describe('Chosen node', () => { - const entry: Node = { id: 'fakeid'} as Node; + const entry: Node = { id: 'fakeid' } as Node; const nodePage: NodePaging = { list: { pagination: {} } }; let hasAllowableOperations; const fakeFolderNode = { id: 'fakeNodeId', isFolder: true } as Node; @@ -1145,7 +515,7 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(component.chosenNode[0]).toBe(entry); }); - component.onCurrentSelection([ { entry } ]); + component.onCurrentSelection([{ entry }]); }); it('should remain empty when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', async () => { @@ -1157,7 +527,7 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(component.chosenNode).toEqual([]); }); - component.onCurrentSelection([ { entry } ]); + component.onCurrentSelection([{ entry }]); }); it('should become empty when clicking on a node (with the WRONG permissions) after previously selecting a right node', async () => { @@ -1299,8 +669,11 @@ describe('ContentNodeSelectorPanelComponent', () => { const isUploadingSpy = spyOn(uploadService, 'isUploading').and.returnValue(true); const documentListReloadSpy = spyOn(component.documentList, 'reloadWithoutResettingSelection'); - const fakeFileModels = [new FileModel({ name: 'fake-name', size: 100 } as File), new FileModel({ name: 'fake-name-2', size: 200 } as File)]; - const fileUploadCompleteEvent = new FileUploadCompleteEvent(fakeFileModels[0], 1, fakeFileModels[0], 0); + const fakeFileModels = [new FileModel({ + name: 'fake-name', + size: 100 + } as File), new FileModel({ name: 'fake-name-2', size: 200 } as File)]; + const fileUploadCompleteEvent = new FileUploadCompleteEvent(fakeFileModels[0], 1, fakeFileModels[0], 0); uploadService.fileUploadComplete.next(fileUploadCompleteEvent); tick(500); @@ -1312,7 +685,7 @@ describe('ContentNodeSelectorPanelComponent', () => { isUploadingSpy.and.returnValue(false); - const secondFileUploadCompleteEvent = new FileUploadCompleteEvent(fakeFileModels[1], 2, fakeFileModels[1], 0); + const secondFileUploadCompleteEvent = new FileUploadCompleteEvent(fakeFileModels[1], 2, fakeFileModels[1], 0); uploadService.fileUploadComplete.next(secondFileUploadCompleteEvent); tick(500); @@ -1371,7 +744,7 @@ describe('ContentNodeSelectorPanelComponent', () => { contentNodeSelectorPanelService.customModels = undefined; }); - it ('should search panel be collapsed by default and expand when clicking the filter button', async () => { + it('should search panel be collapsed by default and expand when clicking the filter button', async () => { contentNodeSelectorPanelService.customModels = [mockContentModelTextProperty]; fixture.detectChanges(); @@ -1386,7 +759,7 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(component.searchPanelExpanded).toEqual(true); }); - it ('should search panel be present when the filter section is expanded', () => { + it('should search panel be present when the filter section is expanded', () => { component.searchPanelExpanded = true; fixture.detectChanges(); @@ -1395,7 +768,7 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(searchPanelContainer).not.toBe(null); }); - it('should filter button be present only when there are custom models', () => { + it('should filter button be present only when there are custom models', () => { contentNodeSelectorPanelService.customModels = [mockContentModelTextProperty]; fixture.detectChanges(); @@ -1404,7 +777,7 @@ describe('ContentNodeSelectorPanelComponent', () => { expect(toggleFiltersPanelButton).not.toEqual(null); }); - it('should filter button not be present when there are no custom models', () => { + it('should filter button not be present when there are no custom models', () => { fixture.detectChanges(); const toggleFiltersPanelButton = fixture.debugElement.query(By.css('[data-automation-id="adf-toggle-search-panel-button"]')); diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts index 1698bd2f6d..ccfe899825 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts @@ -47,11 +47,12 @@ import { RowFilter } from '../document-list/data/row-filter.model'; import { ImageResolver } from '../document-list/data/image-resolver.model'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { CustomResourcesService } from '../document-list/services/custom-resources.service'; -import { NodeEntryEvent, ShareDataRow } from '../document-list'; +import { ShareDataRow } from '../document-list/data/share-data-row.model'; import { Subject } from 'rxjs'; import { SEARCH_QUERY_SERVICE_TOKEN } from '../search/search-query-service.token'; import { SearchQueryBuilderService } from '../search/services/search-query-builder.service'; import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service'; +import { NodeEntryEvent } from '../document-list/components/node.event'; export type ValidationFunction = (entry: Node) => boolean; diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.ts b/lib/content-services/src/lib/document-list/components/document-list.component.ts index 457d5a13ee..8f1bae2c7c 100644 --- a/lib/content-services/src/lib/document-list/components/document-list.component.ts +++ b/lib/content-services/src/lib/document-list/components/document-list.component.ts @@ -19,7 +19,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { - AfterContentInit, Component, ContentChild, ElementRef, EventEmitter, HostListener, Input, NgZone, + AfterContentInit, Component, ContentChild, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; @@ -63,11 +63,16 @@ import { RowFilter } from '../data/row-filter.model'; import { DocumentListService } from '../services/document-list.service'; import { DocumentLoaderNode } from '../models/document-folder.model'; import { takeUntil } from 'rxjs/operators'; +import { ADF_DOCUMENT_PARENT_COMPONENT } from './document-list.token'; @Component({ selector: 'adf-document-list', templateUrl: './document-list.component.html', styleUrls: ['./document-list.component.scss'], + providers:[{ + provide: ADF_DOCUMENT_PARENT_COMPONENT, + useExisting: DocumentListComponent + }], encapsulation: ViewEncapsulation.None, host: { class: 'adf-document-list' } }) @@ -352,7 +357,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte } constructor(private documentListService: DocumentListService, - private ngZone: NgZone, private elementRef: ElementRef, private appConfig: AppConfigService, private userPreferencesService: UserPreferencesService, @@ -507,10 +511,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte } reload() { - this.ngZone.run(() => { - this.resetSelection(); - this.reloadWithoutResettingSelection(); - }); + this.resetSelection(); + this.reloadWithoutResettingSelection(); } reloadWithoutResettingSelection() { diff --git a/lib/content-services/src/lib/document-list/components/document-list.token.ts b/lib/content-services/src/lib/document-list/components/document-list.token.ts new file mode 100644 index 0000000000..b5b8fc3640 --- /dev/null +++ b/lib/content-services/src/lib/document-list/components/document-list.token.ts @@ -0,0 +1,25 @@ +/*! + * @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. + */ + +/* eslint-disable rxjs/no-subject-value */ +/* eslint-disable @typescript-eslint/naming-convention */ + +import { InjectionToken } from '@angular/core'; + +export const ADF_DOCUMENT_PARENT_COMPONENT = new InjectionToken( + 'ADF_DOCUMENT_PARENT_COMPONENT' +); diff --git a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts index a9519d090e..bcd7ebfa4c 100644 --- a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts @@ -26,6 +26,7 @@ import { SEARCH_QUERY_SERVICE_TOKEN } from './../../../search/search-query-servi import { DocumentListComponent } from './../document-list.component'; import { FilterHeaderComponent } from './filter-header.component'; import { Pagination } from '@alfresco/js-api'; +import { ADF_DOCUMENT_PARENT_COMPONENT } from '../document-list.token'; describe('FilterHeaderComponent', () => { let fixture: ComponentFixture; @@ -52,6 +53,7 @@ describe('FilterHeaderComponent', () => { ContentTestingModule ], providers: [ + { provide: ADF_DOCUMENT_PARENT_COMPONENT, useExisting: DocumentListComponent }, { provide: SearchService, useValue: searchMock }, { provide: SEARCH_QUERY_SERVICE_TOKEN, useClass: SearchHeaderQueryBuilderService }, { provide: DocumentListComponent, useValue: documentListMock }, @@ -121,7 +123,7 @@ describe('FilterHeaderComponent', () => { await fixture.whenStable(); expect(queryBuilder.getActiveFilters().length).toBe(0); - const initialFilterValue = { name: 'pinocchio'}; + const initialFilterValue = { name: 'pinocchio' }; component.value = initialFilterValue; const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true); component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange }); diff --git a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts index 07400f90d8..5c2fbb3c5e 100644 --- a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts +++ b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts @@ -17,13 +17,13 @@ import { Component, Inject, OnInit, OnChanges, SimpleChanges, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; import { PaginationModel, DataSorting } from '@alfresco/adf-core'; -import { DocumentListComponent } from '../document-list.component'; import { SEARCH_QUERY_SERVICE_TOKEN } from '../../../search/search-query-service.token'; import { SearchHeaderQueryBuilderService } from '../../../search/services/search-header-query-builder.service'; import { FilterSearch } from './../../../search/models/filter-search.interface'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { NodePaging, MinimalNode } from '@alfresco/js-api'; +import { ADF_DOCUMENT_PARENT_COMPONENT } from '../document-list.token'; @Component({ selector: 'adf-filter-header', @@ -47,7 +47,7 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy { isFilterServiceActive: boolean; private onDestroy$ = new Subject(); - constructor(@Inject(DocumentListComponent) private documentList: DocumentListComponent, + constructor(@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any, @Inject(SEARCH_QUERY_SERVICE_TOKEN) private searchFilterQueryBuilder: SearchHeaderQueryBuilderService) { this.isFilterServiceActive = this.searchFilterQueryBuilder.isFilterServiceActive(); } diff --git a/lib/content-services/src/lib/document-list/document-list.module.ts b/lib/content-services/src/lib/document-list/document-list.module.ts index 785e352082..9c9e032d23 100644 --- a/lib/content-services/src/lib/document-list/document-list.module.ts +++ b/lib/content-services/src/lib/document-list/document-list.module.ts @@ -68,4 +68,5 @@ import { SearchModule } from './../search/search.module'; FilterHeaderComponent ] }) -export class DocumentListModule {} +export class DocumentListModule { +} diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts index 8a0be24b00..2eb5b01649 100644 --- a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts @@ -101,7 +101,7 @@ describe('NewVersionUploaderService', () => { spyOn(contentService, 'hasAllowableOperations').and.returnValue(true); spyOn(service.versionsApi, 'listVersionHistory').and.returnValue(Promise.resolve({ list: { entries: [{ entry: '2' }] } - })); + } as any)); mockNewVersionUploaderDialogData = { node: mockNode, file: mockFile @@ -115,7 +115,7 @@ describe('NewVersionUploaderService', () => { data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-upload'], width: '630px' - }); + } as any); })); it('Should override default dialog panelClass', fakeAsync(() => { @@ -129,7 +129,7 @@ describe('NewVersionUploaderService', () => { data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, panelClass: 'adf-custom-class', width: '500px' - }); + } as any); })); it('Should set dialog height', fakeAsync(() => { @@ -143,7 +143,7 @@ describe('NewVersionUploaderService', () => { panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-upload'], width: '630px', height: '600px' - }); + } as any); })); it('Should not override dialog configuration, if dialog configuration is empty', fakeAsync(() => { @@ -154,7 +154,7 @@ describe('NewVersionUploaderService', () => { data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-upload'], width: '630px' - }); + } as any); })); it('Should dialog add list css class if showVersionsOnly is true', fakeAsync(() => { @@ -169,7 +169,7 @@ describe('NewVersionUploaderService', () => { data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: true }, panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-list'], width: '630px' - }); + } as any); })); }); @@ -181,7 +181,7 @@ describe('NewVersionUploaderService', () => { spyOn(contentService, 'hasAllowableOperations').and.returnValue(true); spyOn(service.versionsApi, 'listVersionHistory').and.returnValue(Promise.resolve({ list: { entries: [{ entry: '2' }] } - })); + } as any); mockNewVersionUploaderDialogData = { node: mockNode, file: mockFile diff --git a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.html b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.html index b30c7bb449..661a30d5be 100644 --- a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.html +++ b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.html @@ -1,5 +1,5 @@ -
+
diff --git a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.spec.ts b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.spec.ts index 0aa7ece7e3..b6e6941c01 100644 --- a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.spec.ts +++ b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.spec.ts @@ -16,7 +16,7 @@ */ import { NodesApiService, SearchService, setupTestBed } from '@alfresco/adf-core'; -import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { TranslateModule } from '@ngx-translate/core'; import { of, throwError } from 'rxjs'; @@ -128,7 +128,7 @@ describe('PermissionListComponent', () => { .toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE'); }); - it('should toggle the inherited button', fakeAsync(() => { + it('should toggle the inherited button', async () => { getNodeSpy.and.returnValue(of(fakeNodeInheritedOnly)); component.ngOnInit(); @@ -152,7 +152,7 @@ describe('PermissionListComponent', () => { .toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.OFF'); expect(element.querySelector('span[title="total"]').textContent.trim()) .toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE'); - })); + }); it('should not toggle inherited button for read only users', async () => { getNodeSpy.and.returnValue(of(fakeReadOnlyNodeInherited)); @@ -186,7 +186,7 @@ describe('PermissionListComponent', () => { }); - describe('locally set permission', () => { + describe('locally set permission', () => { beforeEach(() => { getNodeSpy.and.returnValue(of(fakeLocalPermission)); }); diff --git a/lib/content-services/src/lib/permission-manager/components/user-role-column/user-role-column.component.ts b/lib/content-services/src/lib/permission-manager/components/user-role-column/user-role-column.component.ts index 9902f5d916..589d75af5b 100644 --- a/lib/content-services/src/lib/permission-manager/components/user-role-column/user-role-column.component.ts +++ b/lib/content-services/src/lib/permission-manager/components/user-role-column/user-role-column.component.ts @@ -19,33 +19,35 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { RoleModel } from '../../models/role.model'; @Component({ - selector: 'adf-user-role-column', - template: ` - - - - {{ role.label | adfLocalizedRole }} - - - + selector: 'adf-user-role-column', + template: ` + + + + {{ role.label | adfLocalizedRole }} + + + - + {{value | adfLocalizedRole}} - `, + `, host: { class: 'adf-user-role-column adf-datatable-content-cell adf-expand-cell-4' }, styles: [ `.adf-role-selector-field { width: 100%; - .mat-form-field { - width: 100%; - max-width: 200px; - } } + + .adf-role-selector-field .mat-form-field { + width: 100%; + max-width: 200px; + } + .adf-readonly-role { padding-left: 0 !important; } diff --git a/lib/content-services/src/lib/search/components/search-control.component.scss b/lib/content-services/src/lib/search/components/search-control.component.scss index 5dbfe6eb10..a3115a577e 100644 --- a/lib/content-services/src/lib/search/components/search-control.component.scss +++ b/lib/content-services/src/lib/search/components/search-control.component.scss @@ -1,11 +1,11 @@ -@import '~@angular/material/theming'; +@use '@angular/material' as mat; $mat-menu-overlay-min-width: 112px !default; // 56 * 2 $mat-menu-overlay-max-width: 280px !default; // 56 * 5 .adf { &-search-result-autocomplete { - @include mat-overridable-elevation(2); + @include mat.overridable-elevation(2); min-width: $mat-menu-overlay-min-width; max-width: $mat-menu-overlay-max-width; diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.scss b/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.scss index c5c8ca9bb8..95231da64d 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.scss +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.scss @@ -1,4 +1,4 @@ -@import '~@angular/material/theming'; +@use '@angular/material' as mat; .adf-search-filter-chip { &.mat-chip { @@ -53,6 +53,6 @@ min-width: 320px; border-radius: 12px; - @include mat-elevation(2); + @include mat.elevation(2); } } diff --git a/lib/content-services/src/lib/search/components/search-form/search-form.component.scss b/lib/content-services/src/lib/search/components/search-form/search-form.component.scss index a8b71d9e55..32a6efea66 100644 --- a/lib/content-services/src/lib/search/components/search-form/search-form.component.scss +++ b/lib/content-services/src/lib/search/components/search-form/search-form.component.scss @@ -1,4 +1,4 @@ -@import '~@angular/material/theming'; +@use '@angular/material' as mat; .adf-search-form { &.mat-button { @@ -37,7 +37,7 @@ } &-menu + * .mat-menu-panel { - @include mat-elevation(2); + @include mat.elevation(2); border-radius: 6px; diff --git a/lib/content-services/src/lib/tag/tag-node-list.component.scss b/lib/content-services/src/lib/tag/tag-node-list.component.scss index ab14d76697..edc2a7e19c 100644 --- a/lib/content-services/src/lib/tag/tag-node-list.component.scss +++ b/lib/content-services/src/lib/tag/tag-node-list.component.scss @@ -20,7 +20,7 @@ font-size: var(--theme-title-font-size); background-repeat: no-repeat; display: inline-block; - fill: currentColor; + fill: currentcolor; height: 20px; width: 20px; color: var(--theme-primary-color-default-contrast) !important; diff --git a/lib/content-services/src/lib/version-manager/version-list.component.spec.ts b/lib/content-services/src/lib/version-manager/version-list.component.spec.ts index cbb034d72c..de95b2c45d 100644 --- a/lib/content-services/src/lib/version-manager/version-list.component.spec.ts +++ b/lib/content-services/src/lib/version-manager/version-list.component.spec.ts @@ -22,7 +22,7 @@ import { VersionListComponent } from './version-list.component'; import { setupTestBed } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material/dialog'; import { of } from 'rxjs'; -import { Node, VersionPaging, VersionEntry } from '@alfresco/js-api'; +import { Node, VersionPaging, VersionEntry, NodeEntry } from '@alfresco/js-api'; import { ContentTestingModule } from '../testing/content.testing.module'; import { TranslateModule } from '@ngx-translate/core'; import { ContentVersionService } from './content-version.service'; @@ -63,7 +63,7 @@ describe('VersionListComponent', () => { component.node = { id: nodeId, allowableOperations: ['update'] } as Node; spyOn(component, 'downloadContent').and.stub(); - spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve({ entry: { id: 'nodeInfoId' } })); + spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(new NodeEntry({ entry: { id: 'nodeInfoId' } }))); }); it('should raise confirmation dialog on delete', () => { @@ -299,7 +299,7 @@ describe('VersionListComponent', () => { fixture.detectChanges(); tick(); - expect(component.restored.emit).toHaveBeenCalledWith({ id: 'nodeInfoId' }); + expect(component.restored.emit).toHaveBeenCalledWith(new Node({ id: 'nodeInfoId' })); })); it('should reload the version list after a version restore', fakeAsync(() => { diff --git a/lib/content-services/src/test.ts b/lib/content-services/src/test.ts index 968367ccd9..f65cd98c51 100644 --- a/lib/content-services/src/test.ts +++ b/lib/content-services/src/test.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, @@ -28,7 +28,9 @@ declare const require: any; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting() + platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false } +} ); declare const pdfjsLib: any; diff --git a/lib/content-services/tsconfig.lib.prod.json b/lib/content-services/tsconfig.lib.prod.json index 04c0e66277..2a2faa884c 100644 --- a/lib/content-services/tsconfig.lib.prod.json +++ b/lib/content-services/tsconfig.lib.prod.json @@ -4,6 +4,6 @@ "declarationMap": false }, "angularCompilerOptions": { - "enableIvy": false + "compilationMode": "partial" } } diff --git a/lib/core/.storybook/main.js b/lib/core/.storybook/main.js index 0ba498c521..15262367e2 100644 --- a/lib/core/.storybook/main.js +++ b/lib/core/.storybook/main.js @@ -4,7 +4,7 @@ const rootMain = require('../../../.storybook/main'); module.exports = { ...rootMain, - core: { ...rootMain.core, builder: 'webpack4' }, + core: { ...rootMain.core, builder: 'webpack5' }, stories: [ ...rootMain.stories, diff --git a/lib/core/app-config/app-config.service.spec.ts b/lib/core/app-config/app-config.service.spec.ts index c0f1ae26cf..779eb5566d 100644 --- a/lib/core/app-config/app-config.service.spec.ts +++ b/lib/core/app-config/app-config.service.spec.ts @@ -67,7 +67,6 @@ describe('AppConfigService', () => { spyOn(httpClient, 'get').and.returnValue(of(mockResponse)); extensionService = TestBed.inject(ExtensionService); - appConfigService = TestBed.inject(AppConfigService); appConfigService.load(); }); diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html index b85643b022..137de56463 100644 --- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html +++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html @@ -21,9 +21,9 @@ [attr.data-automation-id]="'card-textitem-value-' + property.key">