From 37059fd4b8329aec9a11931102b893be52ae27cb Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Fri, 7 Sep 2018 07:03:49 +0100 Subject: [PATCH 001/160] update to Angular CLI 6.2 (#616) * upgrade to latest angular libs * support "--watch" mode --- package-lock.json | 2228 +++++++++------------ package.json | 40 +- projects/aca-dev-tools/tsconfig.lib.json | 3 +- projects/adf-extensions/tsconfig.lib.json | 3 +- 4 files changed, 919 insertions(+), 1355 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f5288199..05f8afe20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,26 +21,37 @@ } }, "@angular-devkit/architect": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.7.4.tgz", - "integrity": "sha512-qcxLtA5XhUCqNyyMOD+s7oIVywNnhUNE1qoopnm6MN0FJ1n7iQMU5TPZBTiXDWQVnbGODObi7tGo7gFnEBML5Q==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.0.tgz", + "integrity": "sha512-rgyucHNPzJawBYqM9LSnOWNm9KM6E8ovzvQgzF8SePderqn6TGFAlrDvaZasgLtrihCAR//lNhKUm8w8ft3rgQ==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.4", - "rxjs": "^6.0.0" + "@angular-devkit/core": "0.8.0", + "rxjs": "~6.2.0" + }, + "dependencies": { + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "@angular-devkit/build-angular": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.7.4.tgz", - "integrity": "sha512-aNVhnWHxhx8s8VHn2ixKhrgK/I4h/fyQQd+FtvysvDia5jOb7ckiTeM4I+2hpPI/66Kr2CxSVxuPTlJkRAH+jQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.8.0.tgz", + "integrity": "sha512-/UMI+f4qBCnDnhJOH/gcFUcKu8Cu062IM61NkWmDhJ9uSYrCDO0ZjHjWSOSo4AQoNRy+kCvbnc4PH9eR6L2nkQ==", "dev": true, "requires": { - "@angular-devkit/architect": "0.7.4", - "@angular-devkit/build-optimizer": "0.7.4", - "@angular-devkit/build-webpack": "0.7.4", - "@angular-devkit/core": "0.7.4", - "@ngtools/webpack": "6.1.4", + "@angular-devkit/architect": "0.8.0", + "@angular-devkit/build-optimizer": "0.8.0", + "@angular-devkit/build-webpack": "0.8.0", + "@angular-devkit/core": "0.8.0", + "@ngtools/webpack": "6.2.0", "ajv": "~6.4.0", "autoprefixer": "^8.4.1", "circular-dependency-plugin": "^5.0.2", @@ -67,8 +78,8 @@ "postcss-loader": "^2.1.5", "postcss-url": "^7.3.2", "raw-loader": "^0.5.1", - "rxjs": "^6.0.0", - "sass-loader": "~6.0.7", + "rxjs": "~6.2.0", + "sass-loader": "^7.1.0", "semver": "^5.5.0", "source-map-loader": "^0.2.3", "source-map-support": "^0.5.0", @@ -79,7 +90,7 @@ "tree-kill": "^1.2.0", "uglifyjs-webpack-plugin": "^1.2.5", "url-loader": "^1.0.1", - "webpack": "~4.9.2", + "webpack": "^4.15.1", "webpack-dev-middleware": "^3.1.3", "webpack-dev-server": "^3.1.4", "webpack-merge": "^4.1.2", @@ -111,6 +122,15 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -118,9 +138,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.8.tgz", - "integrity": "sha512-WqAEWPdb78u25RfKzOF0swBpY0dKrNdjc4GvLwm7ScX/o9bj8Eh/YL8mcMhBHYDGl87UkkSXDOFnW4G7GhWhGg==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -139,455 +159,71 @@ } }, "@angular-devkit/build-ng-packagr": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.7.4.tgz", - "integrity": "sha512-Y+lN5d9Pbmw8MfvCdTZ+VHFELJiavM0YwZoAOHHSHTOTujDHJ7D7taXvMlTkWNyGUSTy4uzApldmey79LGDIaw==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.8.0.tgz", + "integrity": "sha512-noFWHRLASwO3fdj/vYsJ7tdA19+3J5D/ED4Z0TREjoB07oC9u2Op20JmN9jPhWOCCEswal7FRCgMk6cOhcSR2g==", "dev": true, "requires": { - "@angular-devkit/architect": "0.7.4", - "@angular-devkit/core": "0.7.4", - "rxjs": "^6.0.0" + "@angular-devkit/architect": "0.8.0", + "@angular-devkit/core": "0.8.0", + "rxjs": "~6.2.0", + "semver": "^5.3.0" }, "dependencies": { - "@angular-devkit/architect": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.7.4.tgz", - "integrity": "sha512-qcxLtA5XhUCqNyyMOD+s7oIVywNnhUNE1qoopnm6MN0FJ1n7iQMU5TPZBTiXDWQVnbGODObi7tGo7gFnEBML5Q==", + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.4", - "rxjs": "^6.0.0" - } - }, - "@angular-devkit/core": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.4.tgz", - "integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==", - "dev": true, - "requires": { - "ajv": "~6.4.0", - "chokidar": "^2.0.3", - "rxjs": "^6.0.0", - "source-map": "^0.5.6" - } - }, - "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", - "dev": true, - "requires": { - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "^2.1.0" + "tslib": "^1.9.0" } } } }, "@angular-devkit/build-optimizer": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.7.4.tgz", - "integrity": "sha512-R+Icu9XjIaKcYFscaMBJ1DyBK2prxK3JQSFi0S//0MdNP4gBFIpCtNdOQsNXovCkpVZ7YlgmdE5+vSb39GVHHA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.8.0.tgz", + "integrity": "sha512-xg1Lh+sRZJdN4foQ1w3d2aen+Q08j+MTQntfa+MppJmGt94f7Om7cYC4tOUJM0vywrx9oGu3MLeFGi09MA5L2w==", "dev": true, "requires": { "loader-utils": "^1.1.0", "source-map": "^0.5.6", - "typescript": "~2.9.1", + "typescript": "~2.9.2", "webpack-sources": "^1.1.0" } }, "@angular-devkit/build-webpack": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.7.4.tgz", - "integrity": "sha512-5oezCFtovcZ8fEkFyNEjs30b/t/DM6HIs3L1bP2xy2SFRNfwcWA0uyb5eag1DytZrzws2GEEmyO9qPtfwKjX7g==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.8.0.tgz", + "integrity": "sha512-l0UXiiWhRcj3wBuFiw3RzMRT0xTLZbadjRWZuvNnCZ/0GYQLM0bN3Lw/1xeuQhLJf8i39t9oYjZQsQ2MbcapCg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.7.4", - "@angular-devkit/core": "0.7.4", - "rxjs": "^6.0.0" + "@angular-devkit/architect": "0.8.0", + "@angular-devkit/core": "0.8.0", + "rxjs": "~6.2.0" + }, + "dependencies": { + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "@angular-devkit/core": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.4.tgz", - "integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", + "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", "dev": true, "requires": { "ajv": "~6.4.0", "chokidar": "^2.0.3", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "source-map": "^0.5.6" }, "dependencies": { @@ -965,6 +601,15 @@ "to-regex": "^3.0.2" } }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "uri-js": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", @@ -977,24 +622,24 @@ } }, "@angular-devkit/schematics": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.7.5.tgz", - "integrity": "sha512-E7HkQeJawUskf2gPnogMc+cTdjJ2Iv3QEZOgprh/ExEmBYByWkGDRX5fQOuy8wME8VZqUBvQACZaVkEredn5EA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.0.tgz", + "integrity": "sha512-XmyNyj/JDXXKACH5XE6cGht6YH5n8KpVLr/X3EPp322yB8HlWWDOsI1UwchzNU769aP7YwjDNwEmu3+8RGD6Ng==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.5", - "rxjs": "^6.0.0" + "@angular-devkit/core": "0.8.0", + "rxjs": "~6.2.0" }, "dependencies": { "@angular-devkit/core": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz", - "integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", + "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", "dev": true, "requires": { "ajv": "~6.4.0", "chokidar": "^2.0.3", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "source-map": "^0.5.6" } }, @@ -1372,6 +1017,15 @@ "to-regex": "^3.0.2" } }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "uri-js": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", @@ -1384,58 +1038,59 @@ } }, "@angular/animations": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.4.tgz", - "integrity": "sha512-R+akCyIneyqJ5wAf9VaymvxbxM4Iw3YsUdylO9rrr9wAUhzmzWhCSGK9bncwL4+d5rbd0n1u+8A8Gm0dZe1P1A==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.7.tgz", + "integrity": "sha512-bjX3VEVEh5scGDDmxEKPzYI8DWUbqOFA34aYDY2cHPnDkLM0I7pEtO44qb72FSbWwXn77sYlby/dx2gtRayOOA==", "requires": { "tslib": "^1.9.0" } }, "@angular/cdk": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.4.6.tgz", - "integrity": "sha512-XKSoeSP4htpOq2UIyF9KDhIJtEQ3wyhZRjDxyRSNmJ9OsuRZxJAGCAzOX5RpMszOyFZgUNVycOi+1lHDe0JrZg==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.4.7.tgz", + "integrity": "sha512-18x0U66fLD5kGQWZ9n3nb75xQouXlWs7kUDaTd8HTrHpT1s2QIAqlLd1KxfrYiVhsEC2jPQaoiae7VnBlcvkBg==", "requires": { "tslib": "^1.7.1" } }, "@angular/cli": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.1.5.tgz", - "integrity": "sha512-QNVUSC8mPdiaxubneqNZISy+wec3gwbKoXjcaQ9/45baOnp662j2iJXwiMh6Atn0YUM4u1iUsz1uHyARMtgZmw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.0.tgz", + "integrity": "sha512-F0BfB59e7HM/STneWj5VvGZHDfotFvY9KKLzJgsazaJaDxo2sPDHm4gTJSHH9FgfuVQkA5e4N11uMeI110zzsA==", "dev": true, "requires": { - "@angular-devkit/architect": "0.7.5", - "@angular-devkit/core": "0.7.5", - "@angular-devkit/schematics": "0.7.5", - "@schematics/angular": "0.7.5", - "@schematics/update": "0.7.5", + "@angular-devkit/architect": "0.8.0", + "@angular-devkit/core": "0.8.0", + "@angular-devkit/schematics": "0.8.0", + "@schematics/angular": "0.8.0", + "@schematics/update": "0.8.0", + "json-schema-traverse": "^0.4.1", "opn": "^5.3.0", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "semver": "^5.1.0", "symbol-observable": "^1.2.0", "yargs-parser": "^10.0.0" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.7.5.tgz", - "integrity": "sha512-zwCpGdx3JDE+Y+LiWh9ErRX+fpFPTRHtEd2PDJmfQsdlIWfjxSR5U9vi3+bSRW2n6IFiH2GCYMS31R64rfMwbg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.0.tgz", + "integrity": "sha512-rgyucHNPzJawBYqM9LSnOWNm9KM6E8ovzvQgzF8SePderqn6TGFAlrDvaZasgLtrihCAR//lNhKUm8w8ft3rgQ==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.5", - "rxjs": "^6.0.0" + "@angular-devkit/core": "0.8.0", + "rxjs": "~6.2.0" } }, "@angular-devkit/core": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz", - "integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", + "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", "dev": true, "requires": { "ajv": "~6.4.0", "chokidar": "^2.0.3", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "source-map": "^0.5.6" } }, @@ -1449,6 +1104,14 @@ "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0", "uri-js": "^3.0.2" + }, + "dependencies": { + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } } }, "anymatch": { @@ -1780,12 +1443,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -1813,6 +1470,15 @@ "to-regex": "^3.0.2" } }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "uri-js": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", @@ -1825,25 +1491,25 @@ } }, "@angular/common": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.4.tgz", - "integrity": "sha512-vpedSD5Rbuj9kLq9W/aeQBVugplimTJPPeuW/zUXHWVOOOk4Y7KBw5g4JdYw2ocSoY3z+dRl/6fR0JTi9+muaA==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.7.tgz", + "integrity": "sha512-zFK2xM0hqR2ZWIfUsn+06jg+0K5PolzTxPjfUtVQDCZo+JHHKTVHEwtfORUaMTMfH9EqKrvfB3t6fCwK0523ag==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.4.tgz", - "integrity": "sha512-HjSK9Jjx6f1jpXy2TALKp2ByAXycZKKD39M9K2g+feTTrpUtd3iDEDLG4S/yok9qs4e1k3L8fxr/qBngQuv23A==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.7.tgz", + "integrity": "sha512-JKuK/fzKNCF+mNPmPmGQjr0uHVpfxmrOqXBriJMklCtdsKeQW94BLUoNjn8h1H10rFbUqYuD5v9AAKdH77FgnA==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.4.tgz", - "integrity": "sha512-ueTe5THcPIKjXOb1+LvqEqh35QPihEGObvJIpudMTqkJHguOr2WXKbbgxzF8QWuIBhOHR9fjtaSgNY5Kk2mfTg==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.7.tgz", + "integrity": "sha512-HzrwmtvrgKwUowfkPTsb6AOqdoQajY3Uso3i0jNc9/bXvpeikH0vj0YIKuQzAJDemluHsowHk/ImEsVGGr2w9g==", "dev": true, "requires": { "chokidar": "^1.4.2", @@ -1861,47 +1527,47 @@ } }, "@angular/core": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.4.tgz", - "integrity": "sha512-8r2LpD4MR0hAYjWkElD/I6iXcugMK/HrpdtopDlRcxW2f6XuMN4mu8eS3g2fu72PwdGhtMcqDDFlbeJ8k599lw==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.7.tgz", + "integrity": "sha512-3MtS8EQy+saNcImDWghphOr/h3l5CpFnZW6aaHiL8T5CpTBNdB86uEmAwtiNQkJ0UeO+cztF1zNCzhm9R93/3w==", "requires": { "tslib": "^1.9.0" } }, "@angular/flex-layout": { - "version": "6.0.0-beta.17", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-6.0.0-beta.17.tgz", - "integrity": "sha512-WrCWlE7NuvvxbeO8+S6aR5cvzX+1CVzpIy0izP8kMLWjAPZ0xjePHc2kJKJVapWMt7aniYZ1inl+GpsvkllycA==", + "version": "6.0.0-beta.18", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-6.0.0-beta.18.tgz", + "integrity": "sha512-1Alv3YSIZYp0CTUIESIaSQLoSVyLzuNKPa5bGM/RzOmeSrndm5plVgI9wopGfJUDiwM18R97rq/4XjDvNT/+ig==", "requires": { "tslib": "^1.7.1" } }, "@angular/forms": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.4.tgz", - "integrity": "sha512-O/rYF36zM15fbXPv4Tj7NlYiCazko6+Eb4o9Ls0nJXMOxt8pRwdYjgGMaOtYprtbH89YlnmJU/gav5Z58JG7sw==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.7.tgz", + "integrity": "sha512-McCElnn6Abr+HAjwxa1ldvIMs101TT0NGq8EHXLyF9QcKG24dU7425+MdLuW0OrtgBql2+RjlqnSiKuxDQHxJA==", "requires": { "tslib": "^1.9.0" } }, "@angular/http": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.4.tgz", - "integrity": "sha512-HD3+ouMAqsgA6hFUS8AQDVlZTbkuNuyomGY1I85IpqXmO7GneszN6y5xIf8XO5ke4UYgvk5UhRrKJ9UG9VtUOw==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.7.tgz", + "integrity": "sha512-N0wXHpEL/CsNM4l44Z+dU51Y994mBEHjt9yb0SeKf02mdrsTJK+cEvfZ0JkVDjGddqdWHvWFn3zSmkR79qLrSQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/language-service": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.4.tgz", - "integrity": "sha512-kvnAphJ7VrKJpm1gN3sFjGp/5gQxu/FAw03yD1f1z+C+aHrNKoxaS9pp9NdOIT/DWlxR/BcEDF4gzMNMTJ9/wA==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.7.tgz", + "integrity": "sha512-L6upXuyO42Z5XhtvbDoDuQEmXEOdSYeGOBmXSxb3ywb/0eh8kHk1Xft+8aaKKtazYjol0t+M+DlZgehqvk4vEA==", "dev": true }, "@angular/material": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.4.6.tgz", - "integrity": "sha512-SUSg9MhLv4IZj6Nh8qoCLDImZugCQ+Jvvt+/cDIaTn6TrT6ZenDHc6jOhbGFesU6FuBDBFIXMiuBPD9kBr7vaA==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.4.7.tgz", + "integrity": "sha512-SdNx7Xovi24Kw9eU6lkLhY/7f2M7L9F+/uh6XuPr4jbGgCUVVpeeVI5ztZhsZRbj1sN+/r1p5w8u62apWWl5Ww==", "requires": { "parse5": "^5.0.0", "tslib": "^1.7.1" @@ -1916,33 +1582,33 @@ } }, "@angular/material-moment-adapter": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.4.6.tgz", - "integrity": "sha512-Bvj/zwtEjF1bqOF1z/M0VcWYOwlRjmCBjvPVmNZ1Bxf7HE8yyRf8B+AAv46sn/EhhAv/eMNU9bzFcxrR/vEHAg==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.4.7.tgz", + "integrity": "sha512-OGdDtpu/yRioOQXhJFCNuiOF2OgiL9VUj8ewFPi1lDtFGUFfVwU2h3hWkKLn+yuPW+DBVYla11tCNsn5dLElmA==", "requires": { "tslib": "^1.7.1" } }, "@angular/platform-browser": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.4.tgz", - "integrity": "sha512-46UPtC360+3E4eeOQk45qp+r+d0Qnsujyot+XtVKQmTSHTInDlwfIGA9TBTw8GyAs3O65i80LRkDHFz9BM2pmw==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.7.tgz", + "integrity": "sha512-YOYg944aefCWElJhnma8X+3wJDb6nHf6aBAVN+YPg0bUplEFacR4y6PeM9QR8vjh5Y0kbGG9ZPGDT/WwP2t4sQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.4.tgz", - "integrity": "sha512-a/WVCsAa0qdtLNuK6h8q+nwtXmJqOlc+dJrBK3vz1umhc80nFZeaUPMTnkRwphk6WVE8xHvjb41PZsI8jF9CBQ==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.7.tgz", + "integrity": "sha512-sSF7n4SpwPiP1fMwocu/RUegpp/45jHK/+r9biXUXUBD12zO5QMcLHU393sjoNi7e6+meuXEH0pnWa66dTznjw==", "requires": { "tslib": "^1.9.0" } }, "@angular/router": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.4.tgz", - "integrity": "sha512-ANdJmpPkr4BMW3/ixJ/qSbsQk4CwR3BPDJp3Iua/xLy7i+9h0bcs11Lpdyo5U3esPYpcpc6TE3ofptyD6xpyGg==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.7.tgz", + "integrity": "sha512-YaOTq2icKAd9FDls2qo2Qp8FrmLGke3eA+bZ3FvOhFydxyUAvlU96N9Y9Gb05tXTtBaQNzAInov2bbp2YMFEFA==", "requires": { "tslib": "^1.9.0" } @@ -1998,15 +1664,26 @@ "dev": true }, "@ngtools/webpack": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.1.4.tgz", - "integrity": "sha512-LiDAvHWKdTyOp8YvjWH1oZtIY8qod13UomvTRQ2FuXXjJwNmi4Bk1VpfGCq3hhtdpo4x/aWHFBuBBpPxoJ74SQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.2.0.tgz", + "integrity": "sha512-X25EhXPnb5XKfnyK1WHKXoH59GH3ciUP4s9ZqsH3ao9f1FMMDdyL1Os9kVxKsC5WZYsm674iJMn3iZPTHY+bzg==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.4", - "rxjs": "^6.0.0", + "@angular-devkit/core": "0.8.0", + "rxjs": "~6.2.0", "tree-kill": "^1.0.0", "webpack-sources": "^1.1.0" + }, + "dependencies": { + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "@ngx-translate/core": { @@ -2018,26 +1695,37 @@ } }, "@schematics/angular": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.7.5.tgz", - "integrity": "sha512-NrtvFwHCoWon8KInsvA1jdPu4pVJGa8GAWM/jqnE7HpwPwM7hMML08lV0P8r3NX5t2/i0CKvfp4AAEr5MXorEQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.0.tgz", + "integrity": "sha512-D7fCtbhHg4aHRyDfSFxjKbcMOgz9IzMBWVjHJU7LGCGvX67H6eCNqbEf/Ogu1jzFQjMFR5VvPjXhamqTXjv7ug==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.5", - "@angular-devkit/schematics": "0.7.5", + "@angular-devkit/core": "0.8.0", + "@angular-devkit/schematics": "0.8.0", "typescript": ">=2.6.2 <2.10" }, "dependencies": { "@angular-devkit/core": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz", - "integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", + "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", "dev": true, "requires": { "ajv": "~6.4.0", "chokidar": "^2.0.3", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "source-map": "^0.5.6" + }, + "dependencies": { + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "ajv": { @@ -2426,28 +2114,28 @@ } }, "@schematics/update": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.7.5.tgz", - "integrity": "sha512-pwNkXGtlzyCV6tsTPe8AgUuMCkmubcz94zgL6pSMdEe122yXBcKnr/PKqG9QzD/gGwmOcHUE9EWcuRtU5kdFpA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.0.tgz", + "integrity": "sha512-5nrvUWdsrn/iW/V4Dk0a1/yxnzmstxVlsI0PKFRImkU67pVmJDGkELqFg1fQBnqKcFJoBUHtkGt3+P4b8BdHsw==", "dev": true, "requires": { - "@angular-devkit/core": "0.7.5", - "@angular-devkit/schematics": "0.7.5", + "@angular-devkit/core": "0.8.0", + "@angular-devkit/schematics": "0.8.0", "npm-registry-client": "^8.5.1", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "semver": "^5.3.0", "semver-intersect": "^1.1.2" }, "dependencies": { "@angular-devkit/core": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.7.5.tgz", - "integrity": "sha512-r99BZvvuNAqSRm05jXfx0sb3Ip0zvHPtAM6NReXzWPoqaVFpjVUdj/CKA+9HWG/Zt9meG9pEQt/HKK8UXaZDVA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", + "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", "dev": true, "requires": { "ajv": "~6.4.0", "chokidar": "^2.0.3", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "source-map": "^0.5.6" } }, @@ -2825,6 +2513,15 @@ "to-regex": "^3.0.2" } }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "uri-js": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", @@ -2893,194 +2590,198 @@ "dev": true }, "@webassemblyjs/ast": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.4.3.tgz", - "integrity": "sha512-S6npYhPcTHDYe9nlsKa9CyWByFi8Vj8HovcAgtmMAQZUOczOZbQ8CnwMYKYC5HEZzxEE+oY0jfQk4cVlI3J59Q==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", "dev": true, "requires": { - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/wast-parser": "1.4.3", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", "debug": "^3.1.0", - "webassemblyjs": "1.4.3" + "mamacro": "^0.0.3" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.4.3.tgz", - "integrity": "sha512-3zTkSFswwZOPNHnzkP9ONq4bjJSeKVMcuahGXubrlLmZP8fmTIJ58dW7h/zOVWiFSuG2em3/HH3BlCN7wyu9Rw==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.4.3.tgz", - "integrity": "sha512-e8+KZHh+RV8MUvoSRtuT1sFXskFnWG9vbDy47Oa166xX+l0dD5sERJ21g5/tcH8Yo95e9IN3u7Jc3NbhnUcSkw==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", "dev": true, "requires": { "debug": "^3.1.0" } }, "@webassemblyjs/helper-code-frame": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.4.3.tgz", - "integrity": "sha512-9FgHEtNsZQYaKrGCtsjswBil48Qp1agrzRcPzCbQloCoaTbOXLJ9IRmqT+uEZbenpULLRNFugz3I4uw18hJM8w==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", "dev": true, "requires": { - "@webassemblyjs/wast-printer": "1.4.3" + "@webassemblyjs/wast-printer": "1.5.13" } }, "@webassemblyjs/helper-fsm": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.4.3.tgz", - "integrity": "sha512-JINY76U+702IRf7ePukOt037RwmtH59JHvcdWbTTyHi18ixmQ+uOuNhcdCcQHTquDAH35/QgFlp3Y9KqtyJsCQ==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", "dev": true }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + } + }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.4.3.tgz", - "integrity": "sha512-I7bS+HaO0K07Io89qhJv+z1QipTpuramGwUSDkwEaficbSvCcL92CUZEtgykfNtk5wb0CoLQwWlmXTwGbNZUeQ==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.4.3.tgz", - "integrity": "sha512-p0yeeO/h2r30PyjnJX9xXSR6EDcvJd/jC6xa/Pxg4lpfcNi7JUswOpqDToZQ55HMMVhXDih/yqkaywHWGLxqyQ==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-buffer": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/wasm-gen": "1.4.3", + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", "debug": "^3.1.0" } }, - "@webassemblyjs/leb128": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.4.3.tgz", - "integrity": "sha512-4u0LJLSPzuRDWHwdqsrThYn+WqMFVqbI2ltNrHvZZkzFPO8XOZ0HFQ5eVc4jY/TNHgXcnwrHjONhPGYuuf//KQ==", + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", "dev": true, "requires": { - "leb": "^0.3.0" + "ieee754": "^1.1.11" } }, - "@webassemblyjs/validation": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/validation/-/validation-1.4.3.tgz", - "integrity": "sha512-R+rRMKfhd9mq0rj2mhU9A9NKI2l/Rw65vIYzz4lui7eTKPcCu1l7iZNi4b9Gen8D42Sqh/KGiaQNk/x5Tn/iBQ==", + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3" + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } } }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, "@webassemblyjs/wasm-edit": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.4.3.tgz", - "integrity": "sha512-qzuwUn771PV6/LilqkXcS0ozJYAeY/OKbXIWU3a8gexuqb6De2p4ya/baBeH5JQ2WJdfhWhSvSbu86Vienttpw==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-buffer": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/helper-wasm-section": "1.4.3", - "@webassemblyjs/wasm-gen": "1.4.3", - "@webassemblyjs/wasm-opt": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "@webassemblyjs/wast-printer": "1.4.3", + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", "debug": "^3.1.0" } }, "@webassemblyjs/wasm-gen": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.4.3.tgz", - "integrity": "sha512-eR394T8dHZfpLJ7U/Z5pFSvxl1L63JdREebpv9gYc55zLhzzdJPAuxjBYT4XqevUdW67qU2s0nNA3kBuNJHbaQ==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/leb128": "1.4.3" + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" } }, "@webassemblyjs/wasm-opt": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.4.3.tgz", - "integrity": "sha512-7Gp+nschuKiDuAL1xmp4Xz0rgEbxioFXw4nCFYEmy+ytynhBnTeGc9W9cB1XRu1w8pqRU2lbj2VBBA4cL5Z2Kw==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-buffer": "1.4.3", - "@webassemblyjs/wasm-gen": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", "debug": "^3.1.0" } }, "@webassemblyjs/wasm-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.4.3.tgz", - "integrity": "sha512-KXBjtlwA3BVukR/yWHC9GF+SCzBcgj0a7lm92kTOaa4cbjaTaa47bCjXw6cX4SGQpkncB9PU2hHGYVyyI7wFRg==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/leb128": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "webassemblyjs": "1.4.3" + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" } }, "@webassemblyjs/wast-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.4.3.tgz", - "integrity": "sha512-QhCsQzqV0CpsEkRYyTzQDilCNUZ+5j92f+g35bHHNqS22FppNTywNFfHPq8ZWZfYCgbectc+PoghD+xfzVFh1Q==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/floating-point-hex-parser": "1.4.3", - "@webassemblyjs/helper-code-frame": "1.4.3", - "@webassemblyjs/helper-fsm": "1.4.3", + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", "long": "^3.2.0", - "webassemblyjs": "1.4.3" + "mamacro": "^0.0.3" } }, "@webassemblyjs/wast-printer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.4.3.tgz", - "integrity": "sha512-EgXk4anf8jKmuZJsqD8qy5bz2frEQhBvZruv+bqwNoLWUItjNSFygk8ywL3JTEz9KtxTlAmqTXNrdD1d9gNDtg==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/wast-parser": "1.4.3", + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", "long": "^3.2.0" } }, - "@webpack-contrib/schema-utils": { - "version": "1.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz", - "integrity": "sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chalk": "^2.3.2", - "strip-ansi": "^4.0.0", - "text-table": "^0.2.0", - "webpack-log": "^1.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -3098,9 +2799,9 @@ } }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", + "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", "dev": true }, "acorn-dynamic-import": { @@ -3233,6 +2934,12 @@ } } }, + "ansi-colors": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.0.5.tgz", + "integrity": "sha512-VVjWpkfaphxUBFarydrQ3n26zX5nIK7hcbT3/ielrvwDDyBBjuh2vuSw1P9zkPq0cfqvdw7lkYHnu+OLSfIBsg==", + "dev": true + }, "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", @@ -3353,16 +3060,6 @@ "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", "dev": true }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, "array-slice": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", @@ -3454,6 +3151,12 @@ } } }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -3512,6 +3215,12 @@ "postcss-value-parser": "^3.2.3" } }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, "aws4": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", @@ -4212,15 +3921,15 @@ } }, "caniuse-lite": { - "version": "1.0.30000877", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000877.tgz", - "integrity": "sha512-h04kV/lcuhItU1CZTJOxUEk/9R+1XeJqgc67E+XC8J9TjPM8kzVgOn27ZtRdDUo8O5F8U4QRCzDWJrVym3w3Cg==", + "version": "1.0.30000885", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000885.tgz", + "integrity": "sha512-cXKbYwpxBLd7qHyej16JazPoUacqoVuDhvR61U7Fr5vSxMUiodzcYa1rQYRYfZ5GexV03vGZHd722vNPLjPJGQ==", "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", "dev": true }, "caseless": { @@ -4275,15 +3984,18 @@ "dev": true }, "chrome-trace-event": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz", - "integrity": "sha512-sjndyZHrrWiu4RY7AkHgjn80GfAM2ZSzUkZLV/Js59Ldmh6JDThf0SUmOHU53rFu2rVxxfCzJ30Ukcfch3Gb/A==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } }, "ci-info": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.3.1.tgz", - "integrity": "sha512-l4wK/SFEN8VVTQ9RO1I5yzIL2vw1w6My29qA6Gwaec80QeHxfXbruuUWqn1knyMoJn/X5kav3zVY1TlRHSKeIA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.4.0.tgz", + "integrity": "sha512-Oqmw2pVfCl8sCL+1QgMywPfdxPJPkC51y4usw0iiE2S9qnEOAqXy8bwl1CpMpnoU39g4iKJTz6QZj+28FvOnjQ==", "dev": true }, "cipher-base": { @@ -4680,10 +4392,13 @@ "dev": true }, "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } }, "cookie": { "version": "0.3.1", @@ -5197,6 +4912,46 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "default-gateway": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", + "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "ip-regex": "^2.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } + } + }, "default-require-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", @@ -5356,9 +5111,9 @@ } }, "detect-node": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", - "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, "di": { @@ -5548,9 +5303,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.58", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.58.tgz", - "integrity": "sha512-AGJxlBEn2wOohxqWZkISVsOjZueKTQljfEODTDSEiMqSpH0S+xzV+/5oEM9AGaqhu7DzrpKOgU7ocQRjj0nJmg==", + "version": "1.3.63", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.63.tgz", + "integrity": "sha512-Ec35NNY040HKuSxMAzBMgz/uUI78amSWpBUD9x2gN7R7gkb/wgAcClngWklcLP0/lm/g0UUYHnC/tUIlZj8UvQ==", "dev": true }, "elliptic": { @@ -5775,14 +5530,47 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { "esrecurse": "^4.1.0", "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } } }, "esprima": { @@ -5798,12 +5586,20 @@ "dev": true, "requires": { "estraverse": "^4.1.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } } }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", "dev": true }, "estree-walker": { @@ -7119,6 +6915,48 @@ } } }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -7166,12 +7004,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -7309,28 +7141,25 @@ "dev": true }, "html-minifier": { - "version": "3.5.19", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.19.tgz", - "integrity": "sha512-Qr2JC9nsjK8oCrEmuB430ZIA8YWbF3D5LSjywD75FTuXmeqacwHgIM8wp3vHYzzPbklSjp53RdmDuzR4ub2HzA==", + "version": "3.5.20", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz", + "integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==", "dev": true, "requires": { "camel-case": "3.0.x", - "clean-css": "4.1.x", - "commander": "2.16.x", + "clean-css": "4.2.x", + "commander": "2.17.x", "he": "1.1.x", "param-case": "2.1.x", "relateurl": "0.2.x", "uglify-js": "3.4.x" }, "dependencies": { - "clean-css": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", - "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", - "dev": true, - "requires": { - "source-map": "0.5.x" - } + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true } } }, @@ -7757,6 +7586,17 @@ } } }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -7852,13 +7692,67 @@ "dev": true }, "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "dev": true, "requires": { - "pkg-dir": "^2.0.0", + "pkg-dir": "^3.0.0", "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } } }, "imurmurhash": { @@ -7916,12 +7810,13 @@ "dev": true }, "internal-ip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", - "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", + "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", "dev": true, "requires": { - "meow": "^3.3.0" + "default-gateway": "^2.6.0", + "ipaddr.js": "^1.5.2" } }, "invariant": { @@ -7944,6 +7839,12 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, "ipaddr.js": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", @@ -8305,25 +8206,6 @@ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" - } - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", @@ -8349,16 +8231,6 @@ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", @@ -9243,9 +9115,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.8.tgz", - "integrity": "sha512-WqAEWPdb78u25RfKzOF0swBpY0dKrNdjc4GvLwm7ScX/o9bj8Eh/YL8mcMhBHYDGl87UkkSXDOFnW4G7GhWhGg==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -9302,12 +9174,6 @@ "invert-kv": "^1.0.0" } }, - "leb": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/leb/-/leb-0.3.0.tgz", - "integrity": "sha1-Mr7p+tFoMo1q6oUi2DP0GA7tHaM=", - "dev": true - }, "less": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/less/-/less-3.8.0.tgz", @@ -9588,15 +9454,6 @@ "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", "dev": true }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, "log4js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.5.tgz", @@ -9616,16 +9473,6 @@ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", "dev": true }, - "loglevelnext": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", - "dev": true, - "requires": { - "es6-symbol": "^3.1.1", - "object.assign": "^4.1.0" - } - }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -9702,6 +9549,21 @@ "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", "dev": true }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", + "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -9855,14 +9717,27 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" }, "mini-css-extract-plugin": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.1.tgz", - "integrity": "sha512-XWuB3G61Rtasq/gLe7cp5cuozehE6hN+E4sxCamRR/WDiHTg+f7ZIAS024r8UJQffY+e2gGELXQZgQoFDfNDCg==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.2.tgz", + "integrity": "sha512-ots7URQH4wccfJq9Ssrzu2+qupbncAce4TmTzunI9CIwlQMp2XI+WNUw6xWF6MMAGAm1cbUVINrSjATaVMyKXg==", "dev": true, "requires": { - "@webpack-contrib/schema-utils": "^1.0.0-beta.0", "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } } }, "minimalistic-assert": { @@ -10082,9 +9957,9 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "ng-packagr": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-4.1.0.tgz", - "integrity": "sha512-8mLZpPwq0QSXqVVoYoe+FMk7HVABuJJ9zJm/uX6NZjq14pcHeGUeNEPhbejcaRGFX/9Zn10WugLicXupCzNVig==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-4.1.1.tgz", + "integrity": "sha512-sl3tsc5uT0azDJNAYcjgW+5HLJRDon2AzdvShNJXakLYo3srdgR6lsw9W5TZB6/IJwWtuCAGSCI4UzdV5wuDjw==", "dev": true, "requires": { "@ngtools/json-schema": "^1.1.0", @@ -10115,24 +9990,6 @@ "update-notifier": "^2.3.0" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -10155,32 +10012,20 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, "autoprefixer": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.1.1.tgz", - "integrity": "sha512-Q/2zhVEglXXCBNCOFfnihQcQystPYJt4GrIopeWhPjFxRPXya8eOstB89AafW0nWhSscByp+rSXp9EE5X4zgXQ==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.1.5.tgz", + "integrity": "sha512-kk4Zb6RUc58ld7gdosERHMF3DzIYJc2fp5sX46qEsGXQQy5bXsu8qyLjoxuY1NuQ/cJuCYnx99BfjwnRggrYIw==", "dev": true, "requires": { - "browserslist": "^4.0.2", - "caniuse-lite": "^1.0.30000876", + "browserslist": "^4.1.0", + "caniuse-lite": "^1.0.30000884", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", "postcss": "^7.0.2", "postcss-value-parser": "^3.2.3" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", @@ -10211,22 +10056,16 @@ } }, "browserslist": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.0.2.tgz", - "integrity": "sha512-lpujC4zv1trcKUUwfD4pFVNga4YSpB3sLB+/I+A8gvGQxno1c0dMB2aCQy0FE5oUNIDjD9puFiFF0zeS6Ji48w==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.1.1.tgz", + "integrity": "sha512-VBorw+tgpOtZ1BYhrVSVTzTt/3+vSE3eFUh0N2GCFK1HffceOaf32YS/bs6WiFhjDAblAFrx85jMy3BG9fBK2Q==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000876", - "electron-to-chromium": "^1.3.57", + "caniuse-lite": "^1.0.30000884", + "electron-to-chromium": "^1.3.62", "node-releases": "^1.0.0-alpha.11" } }, - "caniuse-lite": { - "version": "1.0.30000877", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000877.tgz", - "integrity": "sha512-h04kV/lcuhItU1CZTJOxUEk/9R+1XeJqgc67E+XC8J9TjPM8kzVgOn27ZtRdDUo8O5F8U4QRCzDWJrVym3w3Cg==", - "dev": true - }, "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", @@ -10257,12 +10096,6 @@ "ms": "2.0.0" } }, - "electron-to-chromium": { - "version": "1.3.58", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.58.tgz", - "integrity": "sha512-AGJxlBEn2wOohxqWZkISVsOjZueKTQljfEODTDSEiMqSpH0S+xzV+/5oEM9AGaqhu7DzrpKOgU7ocQRjj0nJmg==", - "dev": true - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -10391,12 +10224,6 @@ } } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -10461,33 +10288,6 @@ } } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", @@ -10558,12 +10358,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -10619,74 +10413,6 @@ "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", "dev": true }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "dev": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - } - }, - "node-sass": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.3.tgz", - "integrity": "sha512-XzXyGjO+84wxyH7fV6IwBOTrEBe2f0a6SBze9QWWYR/cL74AcQUks2AsqcCZenl/Fp/JVbuEaLpgrLtocwBUww==", - "dev": true, - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.10.0", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "2.87.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "p-limit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", @@ -10721,12 +10447,6 @@ "json-parse-better-errors": "^1.0.1" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "postcss": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.2.tgz", @@ -10772,40 +10492,6 @@ "read-pkg": "^3.0.0" } }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10820,6 +10506,12 @@ } } }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", @@ -10845,7 +10537,6 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "dev": true, - "optional": true, "requires": { "fstream": "^1.0.0", "glob": "^7.0.3", @@ -10861,151 +10552,11 @@ "which": "1" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true, - "optional": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true, - "optional": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "dev": true, - "optional": true, - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "optional": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true, - "optional": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "optional": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } + "dev": true } } }, @@ -11062,7 +10613,6 @@ "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.3.tgz", "integrity": "sha512-XzXyGjO+84wxyH7fV6IwBOTrEBe2f0a6SBze9QWWYR/cL74AcQUks2AsqcCZenl/Fp/JVbuEaLpgrLtocwBUww==", "dev": true, - "optional": true, "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -11085,46 +10635,17 @@ "true-case-path": "^1.0.2" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "optional": true + "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, - "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -11133,63 +10654,11 @@ "supports-color": "^2.0.0" } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true, - "optional": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "optional": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true, - "optional": true - }, "request": { "version": "2.87.0", "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "dev": true, - "optional": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.6.0", @@ -11217,8 +10686,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "optional": true + "dev": true } } }, @@ -11408,18 +10876,6 @@ } } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -11574,11 +11030,23 @@ "os-tmpdir": "^1.0.0" } }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -11797,6 +11265,12 @@ "worker-loader": "^1.1.0" } }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -11828,9 +11302,9 @@ } }, "portfinder": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.16.tgz", - "integrity": "sha512-icBXCFQxzlK2PMepOM0QeEdPPFSLAaXXeuKOv5AClJlMy1oVCBrkDGJ12IZYesI/BF8mpeVco3vRCmgeBb4+hw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz", + "integrity": "sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==", "dev": true, "requires": { "async": "^1.5.2", @@ -12848,9 +12322,9 @@ } }, "rollup-plugin-commonjs": { - "version": "9.1.5", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.5.tgz", - "integrity": "sha512-Hy7KbvsSMNu6aCO2xabp8gBcWrTiS+EzfHkzWwZwMjrcAYuYfCLU7fP1nM4xM0FMye/13r8mzTkfb9AmDaZ1hQ==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.6.tgz", + "integrity": "sha512-J7GOJm9uzEeLqkVxYSgjyoieh34hATWpa9G2M1ilGzWOLYGfQx5IDQ9ewG8QUj/Z2dzgV+d0/AyloAzElkABAA==", "dev": true, "requires": { "estree-walker": "^0.5.1", @@ -12860,9 +12334,9 @@ } }, "rollup-plugin-node-resolve": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz", - "integrity": "sha512-9zHGr3oUJq6G+X0oRMYlzid9fXicBdiydhwGChdyeNRGPcN/majtegApRKHLR5drboUvEWU+QeUmGTyEZQs3WA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.4.0.tgz", + "integrity": "sha512-PJcd85dxfSBWih84ozRtBkB731OjXk0KnzN0oGp7WOWcarAFkVa71cV5hTJg2qpVsV2U8EUwrzHP3tvy9vS3qg==", "dev": true, "requires": { "builtin-modules": "^2.0.0", @@ -12914,9 +12388,9 @@ "dev": true }, "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.2.tgz", + "integrity": "sha512-hV7criqbR0pe7EeL3O66UYVg92IR0XsA97+9y+BWTePK9SKmEI5Qd3Zj6uPnGkNzXsBywBQWTvujPl+1Kn9Zjw==", "requires": { "tslib": "^1.9.0" } @@ -12960,16 +12434,17 @@ } }, "sass-loader": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.7.tgz", - "integrity": "sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", "dev": true, "requires": { "clone-deep": "^2.0.1", "loader-utils": "^1.0.1", "lodash.tail": "^4.1.1", "neo-async": "^2.5.0", - "pify": "^3.0.0" + "pify": "^3.0.0", + "semver": "^5.5.0" } }, "saucelabs": { @@ -13982,14 +13457,6 @@ "loader-utils": "^1.0.2", "lodash.clonedeep": "^4.5.0", "when": "~3.6.x" - }, - "dependencies": { - "when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", - "dev": true - } } }, "superagent": { @@ -14050,12 +13517,6 @@ "execa": "^0.7.0" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -15313,19 +14774,6 @@ "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz", "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA=" }, - "webassemblyjs": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webassemblyjs/-/webassemblyjs-1.4.3.tgz", - "integrity": "sha512-4lOV1Lv6olz0PJkDGQEp82HempAn147e6BXijWDzz9g7/2nSebVP9GVg62Fz5ZAs55mxq13GA0XLyvY8XkyDjg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/validation": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "@webassemblyjs/wast-parser": "1.4.3", - "long": "^3.2.0" - } - }, "webdriver-js-extender": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.0.0.tgz", @@ -15360,21 +14808,23 @@ } }, "webpack": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.9.2.tgz", - "integrity": "sha512-jlWrCrJDU3sdWFprel6jHH8esN2C++Q8ehedRo74u7MWLTUJn9SD7RSgsCTEZCSRpVpMascDylAqPoldauOMfA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.17.2.tgz", + "integrity": "sha512-hCK8FPco2Paz9FVMlo3ZdVd7Jsr7qxoiEwhd7f4dMaWBLZtc7E+/9QNee4CYHlVSvpmspWBnhFpx4MiWSl3nNg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/wasm-edit": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "acorn": "^5.0.0", + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", "acorn-dynamic-import": "^3.0.0", "ajv": "^6.1.0", "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^0.1.1", - "enhanced-resolve": "^4.0.0", - "eslint-scope": "^3.7.1", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", "json-parse-better-errors": "^1.0.2", "loader-runner": "^2.3.0", "loader-utils": "^1.1.0", @@ -15387,7 +14837,7 @@ "tapable": "^1.0.0", "uglifyjs-webpack-plugin": "^1.2.4", "watchpack": "^1.5.0", - "webpack-sources": "^1.0.1" + "webpack-sources": "^1.2.0" }, "dependencies": { "arr-diff": { @@ -15703,18 +15153,18 @@ } }, "webpack-dev-middleware": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz", - "integrity": "sha512-I6Mmy/QjWU/kXwCSFGaiOoL5YEQIVmbb0o45xMoCyQAg/mClqZVTcsX327sPfekDyJWpCxb+04whNyLOIxpJdQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.2.0.tgz", + "integrity": "sha512-YJLMF/96TpKXaEQwaLEo+Z4NDK8aV133ROF6xp9pe3gQoS7sxfpXh4Rv9eC+8vCvWfmDjRQaMSlRPbO+9G6jgA==", "dev": true, "requires": { "loud-rejection": "^1.6.0", "memory-fs": "~0.4.1", - "mime": "^2.1.0", + "mime": "^2.3.1", "path-is-absolute": "^1.0.0", "range-parser": "^1.0.3", "url-join": "^4.0.0", - "webpack-log": "^1.0.1" + "webpack-log": "^2.0.0" }, "dependencies": { "mime": { @@ -15726,13 +15176,12 @@ } }, "webpack-dev-server": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.5.tgz", - "integrity": "sha512-LVHg+EPwZLHIlfvokSTgtJqO/vI5CQi89fASb5JEDtVMDjY0yuIEqPPdMiKaBJIB/Ab7v/UN/sYZ7WsZvntQKw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.8.tgz", + "integrity": "sha512-c+tcJtDqnPdxCAzEEZKdIPmg3i5i7cAHe+B+0xFNK0BlCc2HF/unYccbU7xTgfGc5xxhCztCQzFmsqim+KhI+A==", "dev": true, "requires": { "ansi-html": "0.0.7", - "array-includes": "^3.0.3", "bonjour": "^3.5.0", "chokidar": "^2.0.0", "compression": "^1.5.2", @@ -15742,13 +15191,14 @@ "express": "^4.16.2", "html-entities": "^1.2.0", "http-proxy-middleware": "~0.18.0", - "import-local": "^1.0.0", - "internal-ip": "1.2.0", + "import-local": "^2.0.0", + "internal-ip": "^3.0.1", "ip": "^1.1.5", "killable": "^1.0.0", "loglevel": "^1.4.1", "opn": "^5.1.0", "portfinder": "^1.0.9", + "schema-utils": "^1.0.0", "selfsigned": "^1.9.1", "serve-index": "^1.7.2", "sockjs": "0.3.19", @@ -15756,9 +15206,9 @@ "spdy": "^3.4.1", "strip-ansi": "^3.0.0", "supports-color": "^5.1.0", - "webpack-dev-middleware": "3.1.3", - "webpack-log": "^1.1.2", - "yargs": "11.0.0" + "webpack-dev-middleware": "3.2.0", + "webpack-log": "^2.0.0", + "yargs": "12.0.2" }, "dependencies": { "ansi-regex": { @@ -15861,6 +15311,43 @@ } } }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -16021,6 +15508,15 @@ } } }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -16042,6 +15538,12 @@ } } }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", @@ -16124,6 +15626,36 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mem": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", + "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" + } + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -16146,14 +15678,49 @@ } }, "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", + "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "^0.10.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" } }, "string-width": { @@ -16183,53 +15750,42 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", "dev": true }, "yargs": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "os-locale": "^3.0.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^2.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" - } - }, - "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" } } } }, "webpack-log": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", "dev": true, "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "loglevelnext": "^1.0.1", - "uuid": "^3.1.0" + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" } }, "webpack-merge": { @@ -16242,9 +15798,9 @@ } }, "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", + "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", "dev": true, "requires": { "source-list-map": "^2.0.0", @@ -16284,6 +15840,12 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 0aa407581..1d5081c30 100644 --- a/package.json +++ b/package.json @@ -27,19 +27,19 @@ "dependencies": { "@alfresco/adf-content-services": "2.5.0", "@alfresco/adf-core": "2.5.0", - "@angular/animations": "6.1.4", - "@angular/cdk": "^6.4.6", - "@angular/common": "6.1.4", - "@angular/compiler": "6.1.4", - "@angular/core": "6.1.4", - "@angular/flex-layout": "^6.0.0-beta.17", - "@angular/forms": "6.1.4", - "@angular/http": "6.1.4", - "@angular/material": "^6.4.6", - "@angular/material-moment-adapter": "^6.4.6", - "@angular/platform-browser": "6.1.4", - "@angular/platform-browser-dynamic": "6.1.4", - "@angular/router": "6.1.4", + "@angular/animations": "6.1.7", + "@angular/cdk": "^6.4.7", + "@angular/common": "6.1.7", + "@angular/compiler": "6.1.7", + "@angular/core": "6.1.7", + "@angular/flex-layout": "^6.0.0-beta.18", + "@angular/forms": "6.1.7", + "@angular/http": "6.1.7", + "@angular/material": "^6.4.7", + "@angular/material-moment-adapter": "^6.4.7", + "@angular/platform-browser": "6.1.7", + "@angular/platform-browser-dynamic": "6.1.7", + "@angular/router": "6.1.7", "@mat-datetimepicker/core": "^2.0.1", "@mat-datetimepicker/moment": "^2.0.1", "@ngrx/effects": "^6.1.0", @@ -57,17 +57,17 @@ "moment-es6": "1.0.0", "monaco-editor": "^0.14.2", "pdfjs-dist": "2.0.303", - "rxjs": "^6.2.2", + "rxjs": "^6.3.2", "showdown": "^1.8.6", "web-animations-js": "2.3.1", "zone.js": "0.8.26" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.7.4", - "@angular-devkit/build-ng-packagr": "~0.7.4", - "@angular/cli": "^6.1.5", - "@angular/compiler-cli": "6.1.4", - "@angular/language-service": "6.1.4", + "@angular-devkit/build-angular": "~0.8.0", + "@angular-devkit/build-ng-packagr": "~0.8.0", + "@angular/cli": "^6.2.0", + "@angular/compiler-cli": "6.1.7", + "@angular/language-service": "6.1.7", "@types/jasmine": "^2.5.53", "@types/jasminewd2": "^2.0.2", "@types/node": "9.3.0", @@ -86,7 +86,7 @@ "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", - "ng-packagr": "^4.1.0", + "ng-packagr": "^4.1.1", "protractor": "^5.4.0", "rimraf": "2.6.2", "selenium-webdriver": "4.0.0-alpha.1", diff --git a/projects/aca-dev-tools/tsconfig.lib.json b/projects/aca-dev-tools/tsconfig.lib.json index 3a2b0b989..bd8c4dab3 100644 --- a/projects/aca-dev-tools/tsconfig.lib.json +++ b/projects/aca-dev-tools/tsconfig.lib.json @@ -24,7 +24,8 @@ "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "flatModuleId": "AUTOGENERATED", - "flatModuleOutFile": "AUTOGENERATED" + "flatModuleOutFile": "AUTOGENERATED", + "enableResourceInlining": true }, "exclude": [ "src/test.ts", diff --git a/projects/adf-extensions/tsconfig.lib.json b/projects/adf-extensions/tsconfig.lib.json index 3a2b0b989..bd8c4dab3 100644 --- a/projects/adf-extensions/tsconfig.lib.json +++ b/projects/adf-extensions/tsconfig.lib.json @@ -24,7 +24,8 @@ "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "flatModuleId": "AUTOGENERATED", - "flatModuleOutFile": "AUTOGENERATED" + "flatModuleOutFile": "AUTOGENERATED", + "enableResourceInlining": true }, "exclude": [ "src/test.ts", From 40a564dfc3e84b1aa3b3bc19f3d6674227266238 Mon Sep 17 00:00:00 2001 From: Adina Parpalita Date: Fri, 7 Sep 2018 16:16:22 +0300 Subject: [PATCH 002/160] add tests for generic errors (#617) --- e2e/pages/page.ts | 20 +++- e2e/suites/list-views/generic-errors.test.ts | 108 +++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100755 e2e/suites/list-views/generic-errors.test.ts diff --git a/e2e/pages/page.ts b/e2e/pages/page.ts index f3ae39138..324fb8f57 100755 --- a/e2e/pages/page.ts +++ b/e2e/pages/page.ts @@ -33,10 +33,14 @@ export abstract class Page { app: by.css('app-root'), layout: by.css('app-layout'), overlay: by.css('.cdk-overlay-container'), - dialogContainer: by.css('.mat-dialog-container'), + dialogContainer: by.css('.mat-dialog-container[role]'), snackBarContainer: '.cdk-overlay-pane .mat-snack-bar-container', snackBar: '.mat-simple-snackbar', - snackBarAction: '.mat-simple-snackbar-action button' + snackBarAction: '.mat-simple-snackbar-action button', + + genericError: 'aca-generic-error', + genericErrorIcon: 'aca-generic-error .mat-icon', + genericErrorTitle: '.generic-error__title' }; public app: ElementFinder = element(this.locators.app); @@ -47,6 +51,10 @@ export abstract class Page { snackBarContainer: ElementFinder = browser.$(this.locators.snackBarContainer); snackBarAction: ElementFinder = browser.$(this.locators.snackBarAction); + genericError: ElementFinder = browser.$(this.locators.genericError); + genericErrorIcon: ElementFinder = browser.$(this.locators.genericErrorIcon); + genericErrorTitle: ElementFinder = browser.$(this.locators.genericErrorTitle); + constructor(public url: string = '') {} get title(): promise.Promise { @@ -110,4 +118,12 @@ export abstract class Page { }, this.snackBarAction); }); } + + isGenericErrorDisplayed() { + return this.genericError.isDisplayed(); + } + + getGenericErrorTitle() { + return this.genericErrorTitle.getText(); + } } diff --git a/e2e/suites/list-views/generic-errors.test.ts b/e2e/suites/list-views/generic-errors.test.ts new file mode 100755 index 000000000..db563be80 --- /dev/null +++ b/e2e/suites/list-views/generic-errors.test.ts @@ -0,0 +1,108 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser } from 'protractor'; +import { SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; + +describe('Generic errors', () => { + const username = `user-${Utils.random()}`; + + const username2 = `user2-${Utils.random()}`; + + const parent = `folder-${Utils.random()}`; let parentId; + const file1 = `file1-${Utils.random()}.txt`; let file1Id; + const file2 = `file2-${Utils.random()}.txt`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + await apis.admin.people.createUser({ username: username2 }); + parentId = (await apis.user.nodes.createFolder(parent)).entry.id; + file1Id = (await apis.user.nodes.createFile(file1, parentId)).entry.id; + await apis.user.nodes.createFile(file2, parentId); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.deleteNodeById(parentId), + apis.user.trashcan.emptyTrash(), + logoutPage.load() + ]); + done(); + }); + + it('File / folder not found - [C217313]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(parent); + await dataTable.doubleClickOnRowByName(file1); + const URL = await browser.getCurrentUrl(); + await apis.user.nodes.deleteNodeById(file1Id, false); + await browser.get(URL); + + expect(page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed'); + expect(page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`); + }); + + it('Invalid URL - [C217315]', async () => { + await page.load('invalid page'); + + expect(page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed'); + expect(page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`); + + }); + + it('Permission denied - [C217314]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(parent); + await dataTable.doubleClickOnRowByName(file2); + const URL = await browser.getCurrentUrl(); + await logoutPage.load(); + await loginPage.loginWith(username2); + await browser.get(URL); + + expect(page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed'); + expect(page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`); + + await logoutPage.load(); + await loginPage.loginWith(username); + }); +}); From b550fe7f727efbd9e09559b8e3c1cb3224154a03 Mon Sep 17 00:00:00 2001 From: Adina Parpalita Date: Fri, 7 Sep 2018 16:17:07 +0300 Subject: [PATCH 003/160] add specific methods for single click on item name link (#618) automate tests for Single click on item name --- e2e/components/data-table/data-table.ts | 16 +- e2e/suites/actions/single-click.test.ts | 212 ++++++++++++++++++ .../actions/toolbar-single-selection.test.ts | 4 +- protractor.conf.js | 18 -- 4 files changed, 228 insertions(+), 22 deletions(-) create mode 100755 e2e/suites/actions/single-click.test.ts diff --git a/e2e/components/data-table/data-table.ts b/e2e/components/data-table/data-table.ts index 5007ca02d..d68be01f8 100755 --- a/e2e/components/data-table/data-table.ts +++ b/e2e/components/data-table/data-table.ts @@ -44,7 +44,7 @@ export class DataTable extends Component { selectedRow: '.adf-datatable-row.is-selected', cell: '.adf-data-table-cell', locationLink: '.aca-location-link', - linkCell: '.adf-location-cell', + nameLink: '.dl-link', selectedIcon: '.mat-icon', @@ -60,7 +60,7 @@ export class DataTable extends Component { body: ElementFinder = this.component.element(by.css(DataTable.selectors.body)); cell = by.css(DataTable.selectors.cell); locationLink = by.css(DataTable.selectors.locationLink); - linkCell: ElementFinder = this.component.element(by.css(DataTable.selectors.linkCell)); + nameLink: ElementFinder = browser.element(by.css(DataTable.selectors.nameLink)); emptyList: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyListContainer)); emptyFolderDragAndDrop: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyFolderDragAndDrop)); emptyListTitle: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyListTitle)); @@ -165,6 +165,14 @@ export class DataTable extends Component { return this.getRowByName(itemName).element(by.css(DataTable.selectors.selectedIcon)).isPresent(); } + getNameLink(itemName: string) { + return this.getRowNameCell(itemName).$(DataTable.selectors.nameLink); + } + + hasLinkOnName(itemName: string) { + return this.getNameLink(itemName).isPresent(); + } + // Navigation/selection methods doubleClickOnRowByName(name: string): promise.Promise { const item = this.getRowFirstCell(name); @@ -178,6 +186,10 @@ export class DataTable extends Component { .then(() => item.click()); } + clickNameLink(itemName: string) { + return this.getNameLink(itemName).click(); + } + selectMultipleItems(names: string[]): promise.Promise { return this.clearSelection() .then(() => browser.actions().sendKeys(protractor.Key.COMMAND).perform()) diff --git a/e2e/suites/actions/single-click.test.ts b/e2e/suites/actions/single-click.test.ts new file mode 100755 index 000000000..54fea644d --- /dev/null +++ b/e2e/suites/actions/single-click.test.ts @@ -0,0 +1,212 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser, protractor } from 'protractor'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { Viewer } from '../../components/viewer/viewer'; +import { SIDEBAR_LABELS } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('Single click on item name', () => { + const username = `user-${Utils.random()}`; + + const file1 = `file1-${Utils.random()}.txt`; let file1Id; + const folder1 = `folder1-${Utils.random()}`; let folder1Id; + + const deletedFile1 = `file1-${Utils.random()}.txt`; let deletedFile1Id; + const deletedFolder1 = `folder1-${Utils.random()}`; let deletedFolder1Id; + + const siteName = `site-${Utils.random()}`; + const fileSite = `fileSite-${Utils.random()}.txt`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + const { breadcrumb } = page.toolbar; + const viewer = new Viewer(); + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + file1Id = (await apis.user.nodes.createFile(file1)).entry.id; + folder1Id = (await apis.user.nodes.createFolder(folder1)).entry.id; + + deletedFile1Id = (await apis.user.nodes.createFile(deletedFile1)).entry.id; + deletedFolder1Id = (await apis.user.nodes.createFolder(deletedFolder1)).entry.id; + await apis.user.nodes.deleteNodeById(deletedFile1Id, false); + await apis.user.nodes.deleteNodeById(deletedFolder1Id, false); + + await apis.user.sites.createSite(siteName); + const docLibId = (await apis.user.sites.getDocLibId(siteName)); + await apis.user.nodes.createFile(fileSite, docLibId); + + await apis.user.shared.shareFileById(file1Id); + await apis.user.shared.waitForApi({ expect: 1 }); + + await apis.user.favorites.addFavoriteById('file', file1Id); + await apis.user.favorites.addFavoriteById('folder', folder1Id); + await apis.user.favorites.waitForApi({ expect: 2 }); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.user.sites.deleteSite(siteName), + apis.user.nodes.deleteNodeById(folder1Id), + apis.user.nodes.deleteNodeById(file1Id), + apis.user.trashcan.emptyTrash(), + logoutPage.load() + ]); + done(); + }); + + it('Hyperlink does not appear for items in the Trash - [C284899]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await dataTable.waitForHeader(); + + expect(await dataTable.hasLinkOnName(deletedFile1)).toBe(false, 'Link on name is present'); + expect(await dataTable.hasLinkOnName(deletedFolder1)).toBe(false, 'Link on name is present'); + }); + + describe('on Personal Files', () => { + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Hyperlink appears when mouse over a file/folder - [C280032]', async () => { + expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); + }); + + it('File preview opens when clicking the hyperlink - [C280033]', async () => { + await dataTable.clickNameLink(file1); + + expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); + + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + }); + + it('Navigate inside the folder when clicking the hyperlink - [C280034]', async () => { + await dataTable.clickNameLink(folder1); + + expect(await breadcrumb.getCurrentItemName()).toBe(folder1); + }); + }); + + describe('on File Libraries', () => { + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + done(); + }); + + it('Hyperlink appears when mouse over a library - [C284901]', async () => { + expect(await dataTable.hasLinkOnName(siteName)).toBe(true, 'Link on site name is missing'); + }); + + it('Navigate inside the library when clicking the hyperlink - [C284902]', async () => { + await dataTable.clickNameLink(siteName); + + expect(await breadcrumb.getCurrentItemName()).toBe(siteName); + expect(await dataTable.getRowByName(fileSite).isPresent()).toBe(true, `${fileSite} not displayed`); + }); + }); + + describe('on Shared Files', () => { + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Hyperlink appears when mouse over a file - [C284905]', async () => { + expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); + }); + + it('File preview opens when clicking the hyperlink - [C284906]', async () => { + await dataTable.clickNameLink(file1); + + expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); + + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + }); + }); + + describe('on Recent Files', () => { + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Hyperlink appears when mouse over a file - [C284907]', async () => { + expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); + }); + + it('File preview opens when clicking the hyperlink - [C284908]', async () => { + await dataTable.clickNameLink(file1); + + expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); + + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + }); + }); + + describe('on Favorites', () => { + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + done(); + }); + + it('Hyperlink appears when mouse over a file/folder - [C284909]', async () => { + expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); + }); + + it('File preview opens when clicking the hyperlink - [C284910]', async () => { + await dataTable.clickNameLink(file1); + + expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); + + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + }); + + it('Navigate inside the folder when clicking the hyperlink - [C284911]', async () => { + await dataTable.clickNameLink(folder1); + + expect(await breadcrumb.getCurrentItemName()).toBe(folder1); + }); + }); + +}); diff --git a/e2e/suites/actions/toolbar-single-selection.test.ts b/e2e/suites/actions/toolbar-single-selection.test.ts index 1baad4b9e..ad3871f47 100755 --- a/e2e/suites/actions/toolbar-single-selection.test.ts +++ b/e2e/suites/actions/toolbar-single-selection.test.ts @@ -214,7 +214,7 @@ describe('Toolbar actions - single selection : ', () => { await toolbar.actions.closeMoreMenu(); }); - // disabled until ACA-1184 is done + // disabled until ACA-1737 is done xit('on Favorites - [C213121]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); @@ -280,7 +280,7 @@ describe('Toolbar actions - single selection : ', () => { await toolbar.actions.closeMoreMenu(); }); - // disabled until ACA-1184 is done + // disabled until ACA-1737 is done xit('on Favorites - [C280478]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); diff --git a/protractor.conf.js b/protractor.conf.js index d4a6a14d2..e6e81dbce 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -74,23 +74,5 @@ exports.config = { useFullTestName: false, reportFailedUrl: true })); - - return browser.driver.executeScript(disableCSSAnimation); - - function disableCSSAnimation() { - var css = '* {' + - '-webkit-transition-duration: 0s !important;' + - 'transition-duration: 0s !important;' + - '-webkit-animation-duration: 0s !important;' + - 'animation-duration: 0s !important;' + - '}', - head = document.head || document.getElementsByTagName('head')[0], - style = document.createElement('style'); - - style.type = 'text/css'; - style.appendChild(document.createTextNode(css)); - head.appendChild(style); - } - } }; From 77d99bb1cc1dc7d0b6e485a8b2c94d0cf65d05dc Mon Sep 17 00:00:00 2001 From: Adina Parpalita Date: Sat, 8 Sep 2018 17:11:51 +0300 Subject: [PATCH 004/160] disable intermittent failing test (#619) * disable intermittent failing test and change to use async / await * disable another intermittent failing test --- e2e/suites/actions/delete-undo-delete.test.ts | 910 ++++++++---------- 1 file changed, 420 insertions(+), 490 deletions(-) diff --git a/e2e/suites/actions/delete-undo-delete.test.ts b/e2e/suites/actions/delete-undo-delete.test.ts index 0f26ad43d..f73b64ba6 100755 --- a/e2e/suites/actions/delete-undo-delete.test.ts +++ b/e2e/suites/actions/delete-undo-delete.test.ts @@ -42,12 +42,14 @@ describe('Delete and undo delete', () => { const page = new BrowsingPage(); const { dataTable, toolbar } = page; - beforeAll(done => { - apis.admin.people.createUser({ username }).then(done); + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + done(); }); - afterAll(done => { - apis.admin.trashcan.emptyTrash().then(done); + afterAll(async (done) => { + await apis.admin.trashcan.emptyTrash(); + done(); }); xit(''); @@ -61,210 +63,181 @@ describe('Delete and undo delete', () => { const folder2 = `folder2-${Utils.random()}`; let folder2Id; const fileLocked1 = `fileLocked-${Utils.random()}.txt`; let fileLocked1Id; - beforeAll(done => { - apis.user.nodes.createFile(file1).then(resp => file1Id = resp.entry.id) - .then(() => apis.user.nodes.createFile(file2).then(resp => file2Id = resp.entry.id)) - .then(() => apis.user.nodes.createFolder(folder1).then(resp => folder1Id = resp.entry.id)) - .then(() => apis.user.nodes.createFolder(folder2).then(resp => folder2Id = resp.entry.id)) - .then(() => apis.user.nodes.createFile(file3, folder1Id)) - .then(() => apis.user.nodes.createFile(file4, folder2Id).then(resp => file4Id = resp.entry.id)) - .then(() => apis.user.nodes.lockFile(file4Id)) + beforeAll(async (done) => { + file1Id = (await apis.user.nodes.createFile(file1)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2)).entry.id; + folder1Id = (await apis.user.nodes.createFolder(folder1)).entry.id; + folder2Id = (await apis.user.nodes.createFolder(folder2)).entry.id; + await apis.user.nodes.createFile(file3, folder1Id); + file4Id = (await apis.user.nodes.createFile(file4, folder2Id)).entry.id; + await apis.user.nodes.lockFile(file4Id); - .then(() => apis.user.nodes.createFile(fileLocked1).then(resp => fileLocked1Id = resp.entry.id)) - .then(() => apis.user.nodes.lockFile(fileLocked1Id)) + fileLocked1Id = (await apis.user.nodes.createFile(fileLocked1)).entry.id; + await apis.user.nodes.lockFile(fileLocked1Id); - .then(() => loginPage.loginWith(username)) - .then(done); + await loginPage.loginWith(username); + done(); }); - beforeEach(done => { - page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES) - .then(() => dataTable.waitForHeader()) - .then(done); + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + done(); }); - afterEach(done => { - page.refresh().then(done); + afterEach(async (done) => { + await page.refresh(); + done(); }); - afterAll(done => { - Promise.all([ + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.unlockFile(file4Id), + apis.user.nodes.unlockFile(fileLocked1Id) + ]); + await Promise.all([ logoutPage.load(), - apis.user.nodes.unlockFile(file4Id) - .then(() => apis.user.nodes.unlockFile(fileLocked1Id)) - .then(() => apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id, fileLocked1Id])) - ]) - .then(() => apis.user.search.waitForApi(username, {expect: 0})) - .then(done); + apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id, fileLocked1Id]) + ]); + await apis.user.search.waitForApi(username, {expect: 0}); + done(); }); - it('delete a file and check notification - [C217125]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('delete a file and check notification - [C217125]', async () => { + let items = await page.dataTable.countRows(); - dataTable.selectItem(file1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`${file1} deleted`); - expect(dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not removed from list'); - items--; - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => expect(dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item is not in trash')) + await dataTable.selectItem(file1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`${file1} deleted`); + expect(dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not removed from list'); + items--; + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item is not in trash'); - .then(() => apis.user.trashcan.restore(file1Id)); + await apis.user.trashcan.restore(file1Id); }); - it('delete multiple files and check notification - [C280502]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('delete multiple files and check notification - [C280502]', async () => { + let items = await page.dataTable.countRows(); - dataTable.selectMultipleItems([file1, file2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`Deleted 2 items`); - expect(dataTable.getRowByName(file1).isPresent()).toBe(false, `${file1} was not removed from list`); - expect(dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} was not removed from list`); - items = items - 2; - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} is not in trash`); - expect(dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} is not in trash`); - }) + await dataTable.selectMultipleItems([file1, file2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Deleted 2 items`); + expect(dataTable.getRowByName(file1).isPresent()).toBe(false, `${file1} was not removed from list`); + expect(dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} was not removed from list`); + items = items - 2; + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} is not in trash`); + expect(dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} is not in trash`); - .then(() => apis.user.trashcan.restore(file1Id)) - .then(() => apis.user.trashcan.restore(file2Id)); + await apis.user.trashcan.restore(file1Id); + await apis.user.trashcan.restore(file2Id); }); - it('delete a folder with content - [C217126]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('delete a folder with content - [C217126]', async () => { + let items = await page.dataTable.countRows(); - dataTable.selectItem(folder1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => { - expect(dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not removed from list'); - items--; - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item is not in trash'); - expect(dataTable.getRowByName(file3).isPresent()).toBe(false, 'Item is in trash'); - }) + await dataTable.selectItem(folder1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + expect(dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not removed from list'); + items--; + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item is not in trash'); + expect(dataTable.getRowByName(file3).isPresent()).toBe(false, 'Item is in trash'); - .then(() => apis.user.trashcan.restore(folder1Id)); + await apis.user.trashcan.restore(folder1Id); }); - it('delete a folder containing locked files - [C217127]', () => { - dataTable.selectItem(folder2) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`${folder2} couldn't be deleted`); - expect(dataTable.getRowByName(folder2).isPresent()).toBe(true, 'Item was removed from list'); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(folder2).isPresent()).toBe(false, 'Item is in trash'); - expect(dataTable.getRowByName(file4).isPresent()).toBe(false, 'Item is in trash'); - }); + it('delete a folder containing locked files - [C217127]', async () => { + await dataTable.selectItem(folder2); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`${folder2} couldn't be deleted`); + expect(dataTable.getRowByName(folder2).isPresent()).toBe(true, 'Item was removed from list'); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(folder2).isPresent()).toBe(false, 'Item is in trash'); + expect(dataTable.getRowByName(file4).isPresent()).toBe(false, 'Item is in trash'); }); - it('notification on multiple items deletion - some items fail to delete - [C217129]', () => { - dataTable.selectMultipleItems([file1, folder2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => expect(message).toContain(`Deleted 1 item, 1 couldn't be deleted`)) + it('notification on multiple items deletion - some items fail to delete - [C217129]', async () => { + await dataTable.selectMultipleItems([file1, folder2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Deleted 1 item, 1 couldn't be deleted`); - .then(() => apis.user.trashcan.restore(file1Id)); + await apis.user.trashcan.restore(file1Id); }); - it('notification on multiple items deletion - all items fail to delete - [C217130]', () => { - dataTable.selectMultipleItems([fileLocked1, folder2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => expect(message).toEqual(`2 items couldn't be deleted`)); + it('notification on multiple items deletion - all items fail to delete - [C217130]', async () => { + await dataTable.selectMultipleItems([fileLocked1, folder2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toEqual(`2 items couldn't be deleted`); }); - it('successful delete notification shows Undo action - [C217131]', () => { - dataTable.selectItem(file1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`Undo`); - }) + it('successful delete notification shows Undo action - [C217131]', async () => { + await dataTable.selectItem(file1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Undo`); - .then(() => apis.user.trashcan.restore(file1Id)); + await apis.user.trashcan.restore(file1Id); }); - it('unsuccessful delete notification does not show Undo action - [C217134]', () => { - dataTable.selectItem(folder2) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).not.toContain(`Undo`); - }); + it('unsuccessful delete notification does not show Undo action - [C217134]', async () => { + await dataTable.selectItem(folder2); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).not.toContain(`Undo`); }); - it('undo delete of file - [C217132]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('undo delete of file - [C217132]', async () => { + const items = await page.dataTable.countRows(); - dataTable.selectItem(file1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => { - expect(dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item was not restored'); - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }); + await dataTable.selectItem(file1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + expect(dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item was not restored'); + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); }); - it('undo delete of folder with content - [C280503]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('undo delete of folder with content - [C280503]', async () => { + const items = await page.dataTable.countRows(); - dataTable.selectItem(folder1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => { - expect(dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item was not restored'); - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => dataTable.doubleClickOnRowByName(folder1)) - .then(() => { - expect(dataTable.getRowByName(file3).isPresent()).toBe(true, 'file from folder not restored'); - }); + await dataTable.selectItem(folder1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + expect(dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item was not restored'); + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await dataTable.doubleClickOnRowByName(folder1); + expect(dataTable.getRowByName(file3).isPresent()).toBe(true, 'file from folder not restored'); }); - it('undo delete of multiple files - [C280504]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('undo delete of multiple files - [C280504]', async () => { + const items = await page.dataTable.countRows(); - dataTable.selectMultipleItems([file1, file2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => { - expect(dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} was not removed from list`); - expect(dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} was not removed from list`); - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }); + await dataTable.selectMultipleItems([file1, file2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + expect(dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} was not removed from list`); + expect(dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} was not removed from list`); + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); }); }); @@ -274,105 +247,98 @@ describe('Delete and undo delete', () => { const sharedFile3 = `sharedFile3-${Utils.random()}.txt`; let sharedFile3Id; const sharedFile4 = `sharedFile4-${Utils.random()}.txt`; let sharedFile4Id; - beforeAll(done => { - apis.user.nodes.createFile(sharedFile1).then(resp => sharedFile1Id = resp.entry.id) - .then(() => apis.user.nodes.createFile(sharedFile2).then(resp => sharedFile2Id = resp.entry.id)) - .then(() => apis.user.nodes.createFile(sharedFile3).then(resp => sharedFile3Id = resp.entry.id)) - .then(() => apis.user.nodes.createFile(sharedFile4).then(resp => sharedFile4Id = resp.entry.id)) - .then(() => apis.user.shared.shareFilesByIds([sharedFile1Id, sharedFile2Id, sharedFile3Id, sharedFile4Id])) - .then(() => apis.user.shared.waitForApi({ expect: 4 })) + beforeAll(async (done) => { + sharedFile1Id = (await apis.user.nodes.createFile(sharedFile1)).entry.id; + sharedFile2Id = (await apis.user.nodes.createFile(sharedFile2)).entry.id; + sharedFile3Id = (await apis.user.nodes.createFile(sharedFile3)).entry.id; + sharedFile4Id = (await apis.user.nodes.createFile(sharedFile4)).entry.id; + await apis.user.shared.shareFilesByIds([sharedFile1Id, sharedFile2Id, sharedFile3Id, sharedFile4Id]); + await apis.user.shared.waitForApi({ expect: 4 }); - .then(() => loginPage.loginWith(username)) - .then(done); + await loginPage.loginWith(username); + done(); }); - beforeEach(done => { - page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES) - .then(() => dataTable.waitForHeader()) - .then(done); + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + done(); }); - afterEach(done => { - page.refresh().then(done); + afterEach(async (done) => { + await page.refresh(); + done(); }); - afterAll(done => { - Promise.all([ + afterAll(async (done) => { + await Promise.all([ logoutPage.load(), apis.user.nodes.deleteNodesById([sharedFile1Id, sharedFile2Id, sharedFile3Id, sharedFile4Id]) - ]) - .then(() => apis.user.search.waitForApi(username, {expect: 0})) - .then(done); + ]); + await apis.user.search.waitForApi(username, {expect: 0}); + done(); }); - it('delete a file and check notification - [C280316]', () => { - dataTable.selectItem(sharedFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`${sharedFile1} deleted`); - expect(dataTable.getRowByName(sharedFile1).isPresent()).toBe(false, 'Item was not removed from list'); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => expect(dataTable.getRowByName(sharedFile1).isPresent()).toBe(true, 'Item is not in trash')) + it('delete a file and check notification - [C280316]', async () => { + await dataTable.selectItem(sharedFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`${sharedFile1} deleted`); + expect(dataTable.getRowByName(sharedFile1).isPresent()).toBe(false, 'Item was not removed from list'); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(sharedFile1).isPresent()).toBe(true, 'Item is not in trash'); - .then(() => apis.user.trashcan.restore(sharedFile1Id)) - .then(() => apis.user.shared.shareFilesByIds([ sharedFile1Id ])) - .then(() => apis.user.shared.waitForApi({ expect: 4 })); + await apis.user.trashcan.restore(sharedFile1Id); + await apis.user.shared.shareFilesByIds([ sharedFile1Id ]); + await apis.user.shared.waitForApi({ expect: 4 }); }); - it('delete multiple files and check notification - [C280513]', () => { - dataTable.selectMultipleItems([sharedFile2, sharedFile3]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`Deleted 2 items`); - expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, `${sharedFile2} was not removed from list`); - expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not removed from list`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(true, `${sharedFile2} is not in trash`); - expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(true, `${sharedFile3} is not in trash`); - }) + it('delete multiple files and check notification - [C280513]', async () => { + await dataTable.selectMultipleItems([sharedFile2, sharedFile3]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Deleted 2 items`); + expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, `${sharedFile2} was not removed from list`); + expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not removed from list`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(true, `${sharedFile2} is not in trash`); + expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(true, `${sharedFile3} is not in trash`); - .then(() => apis.user.trashcan.restore(sharedFile2Id)) - .then(() => apis.user.trashcan.restore(sharedFile3Id)) - .then(() => apis.user.shared.shareFilesByIds([ sharedFile2Id, sharedFile3Id ])) - .then(() => apis.user.shared.waitForApi({ expect: 4 })); + await apis.user.trashcan.restore(sharedFile2Id); + await apis.user.trashcan.restore(sharedFile3Id); + await apis.user.shared.shareFilesByIds([ sharedFile2Id, sharedFile3Id ]); + await apis.user.shared.waitForApi({ expect: 4 }); }); - it('successful delete notification shows Undo action - [C280323]', () => { - dataTable.selectItem(sharedFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => expect(message).toContain(`Undo`)) + it('successful delete notification shows Undo action - [C280323]', async () => { + await dataTable.selectItem(sharedFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Undo`); - .then(() => apis.user.trashcan.restore(sharedFile1Id)); + await apis.user.trashcan.restore(sharedFile1Id); }); - it('undo delete of file - [C280324]', () => { - dataTable.selectItem(sharedFile2) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, 'Item was not restored')); + xit('undo delete of file - [C280324]', async () => { + await dataTable.selectItem(sharedFile2); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, 'Item was not restored'); }); - it('undo delete of multiple files - [C280514]', () => { - dataTable.selectMultipleItems([sharedFile3, sharedFile4]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not restored`); - expect(dataTable.getRowByName(sharedFile4).isPresent()).toBe(false, `${sharedFile4} was not restored`); - }); + xit('undo delete of multiple files - [C280514]', async () => { + await dataTable.selectMultipleItems([sharedFile3, sharedFile4]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not restored`); + expect(dataTable.getRowByName(sharedFile4).isPresent()).toBe(false, `${sharedFile4} was not restored`); }); }); @@ -385,213 +351,186 @@ describe('Delete and undo delete', () => { const favoriteFolder2 = `favFolder2-${Utils.random()}`; let favoriteFolder2Id; const favoriteFileLocked1 = `favFileLocked-${Utils.random()}.txt`; let favoriteFileLocked1Id; - beforeAll(done => { - apis.user.nodes.createFile(favoriteFile1).then(resp => favoriteFile1Id = resp.entry.id) - .then(() => apis.user.nodes.createFile(favoriteFile2).then(resp => favoriteFile2Id = resp.entry.id)) - .then(() => apis.user.nodes.createFolder(favoriteFolder1).then(resp => favoriteFolder1Id = resp.entry.id)) - .then(() => apis.user.nodes.createFolder(favoriteFolder2).then(resp => favoriteFolder2Id = resp.entry.id)) - .then(() => apis.user.nodes.createFile(favoriteFile3, favoriteFolder1Id)) - .then(() => apis.user.nodes.createFile(favoriteFile4, favoriteFolder2Id).then(resp => favoriteFile4Id = resp.entry.id)) - .then(() => apis.user.nodes.lockFile(favoriteFile4Id)) + beforeAll(async (done) => { + favoriteFile1Id = (await apis.user.nodes.createFile(favoriteFile1)).entry.id; + favoriteFile2Id = (await apis.user.nodes.createFile(favoriteFile2)).entry.id; + favoriteFolder1Id = (await apis.user.nodes.createFolder(favoriteFolder1)).entry.id; + favoriteFolder2Id = (await apis.user.nodes.createFolder(favoriteFolder2)).entry.id; + await apis.user.nodes.createFile(favoriteFile3, favoriteFolder1Id); + favoriteFile4Id = (await apis.user.nodes.createFile(favoriteFile4, favoriteFolder2Id)).entry.id; + await apis.user.nodes.lockFile(favoriteFile4Id); - .then(() => apis.user.nodes.createFile(favoriteFileLocked1).then(resp => favoriteFileLocked1Id = resp.entry.id)) - .then(() => apis.user.nodes.lockFile(favoriteFileLocked1Id)) + favoriteFileLocked1Id = (await apis.user.nodes.createFile(favoriteFileLocked1)).entry.id; + await apis.user.nodes.lockFile(favoriteFileLocked1Id); - .then(() => apis.user.favorites.addFavoritesByIds('file', [favoriteFile1Id, favoriteFile2Id, favoriteFileLocked1Id])) - .then(() => apis.user.favorites.addFavoritesByIds('folder', [favoriteFolder1Id, favoriteFolder2Id])) - .then(() => apis.user.favorites.waitForApi({ expect: 5 })) + await apis.user.favorites.addFavoritesByIds('file', [ favoriteFile1Id, favoriteFile2Id, favoriteFileLocked1Id ]); + await apis.user.favorites.addFavoritesByIds('folder', [ favoriteFolder1Id, favoriteFolder2Id ]); + await apis.user.favorites.waitForApi({ expect: 5 }); - .then(() => loginPage.loginWith(username)) - .then(done); + await loginPage.loginWith(username); + done(); }); - beforeEach(done => { - page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES) - .then(() => dataTable.waitForHeader()) - .then(done); + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + done(); }); - afterEach(done => { - page.refresh().then(done); + afterEach(async (done) => { + await page.refresh(); + done(); }); - afterAll(done => { - Promise.all([ + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.unlockFile(favoriteFile4Id), + apis.user.nodes.unlockFile(favoriteFileLocked1Id) + ]); + await Promise.all([ logoutPage.load(), - apis.user.nodes.unlockFile(favoriteFile4Id) - .then(() => apis.user.nodes.unlockFile(favoriteFileLocked1Id)) - .then(() => apis.user.nodes.deleteNodesById([ - favoriteFile1Id, favoriteFile2Id, favoriteFolder1Id, favoriteFolder2Id, favoriteFileLocked1Id - ])) - ]) - .then(() => apis.user.search.waitForApi(username, {expect: 0})) - .then(done); + apis.user.nodes.deleteNodesById([ + favoriteFile1Id, favoriteFile2Id, favoriteFolder1Id, favoriteFolder2Id, favoriteFileLocked1Id + ]) + ]); + await apis.user.search.waitForApi(username, {expect: 0}); + done(); }); - it('delete a file and check notification - [C280516]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('delete a file and check notification - [C280516]', async () => { + let items = await page.dataTable.countRows(); - dataTable.selectItem(favoriteFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`${favoriteFile1} deleted`); - expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, 'Item was not removed from list'); - items--; - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item is not in trash')) + await dataTable.selectItem(favoriteFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`${favoriteFile1} deleted`); + expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, 'Item was not removed from list'); + items--; + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item is not in trash'); - .then(() => apis.user.trashcan.restore(favoriteFile1Id)); + await apis.user.trashcan.restore(favoriteFile1Id); }); - it('delete multiple files and check notification - [C280517]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('delete multiple files and check notification - [C280517]', async () => { + let items = await page.dataTable.countRows(); - dataTable.selectMultipleItems([favoriteFile1, favoriteFile2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`Deleted 2 items`); - expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, `${favoriteFile1} was not removed from list`); - expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(false, `${favoriteFile2} was not removed from list`); - items = items - 2; - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} is not in trash`); - expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} is not in trash`); - }) + await dataTable.selectMultipleItems([favoriteFile1, favoriteFile2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Deleted 2 items`); + expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, `${favoriteFile1} was not removed from list`); + expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(false, `${favoriteFile2} was not removed from list`); + items = items - 2; + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} is not in trash`); + expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} is not in trash`); - .then(() => apis.user.trashcan.restore(favoriteFile1Id)) - .then(() => apis.user.trashcan.restore(favoriteFile2Id)); + await apis.user.trashcan.restore(favoriteFile1Id); + await apis.user.trashcan.restore(favoriteFile2Id); }); - it('delete a folder with content - [C280518]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); - dataTable.selectItem(favoriteFolder1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => { - expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(false, 'Item was not removed from list'); - items--; - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item is not in trash'); - expect(dataTable.getRowByName(favoriteFile3).isPresent()).toBe(false, 'Item is in trash'); - }) + it('delete a folder with content - [C280518]', async () => { + let items = await page.dataTable.countRows(); + await dataTable.selectItem(favoriteFolder1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(false, 'Item was not removed from list'); + items--; + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item is not in trash'); + expect(dataTable.getRowByName(favoriteFile3).isPresent()).toBe(false, 'Item is in trash'); - .then(() => apis.user.trashcan.restore(favoriteFolder1Id)); + await apis.user.trashcan.restore(favoriteFolder1Id); }); - it('delete a folder containing locked files - [C280519]', () => { - dataTable.selectItem(favoriteFolder2) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`${favoriteFolder2} couldn't be deleted`); - expect(dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(true, 'Item was removed from list'); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(false, 'Item is in trash'); - expect(dataTable.getRowByName(favoriteFile4).isPresent()).toBe(false, 'Item is in trash'); - }); + it('delete a folder containing locked files - [C280519]', async () => { + await dataTable.selectItem(favoriteFolder2); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`${favoriteFolder2} couldn't be deleted`); + expect(dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(true, 'Item was removed from list'); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(false, 'Item is in trash'); + expect(dataTable.getRowByName(favoriteFile4).isPresent()).toBe(false, 'Item is in trash'); }); - it('notification on multiple items deletion - some items fail to delete - [C280520]', () => { - dataTable.selectMultipleItems([favoriteFile1, favoriteFolder2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`Deleted 1 item, 1 couldn't be deleted`); - }) + it('notification on multiple items deletion - some items fail to delete - [C280520]', async () => { + await dataTable.selectMultipleItems([favoriteFile1, favoriteFolder2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Deleted 1 item, 1 couldn't be deleted`); - .then(() => apis.user.trashcan.restore(favoriteFile1Id)); + await apis.user.trashcan.restore(favoriteFile1Id); }); - it('notification on multiple items deletion - all items fail to delete - [C280521]', () => { - dataTable.selectMultipleItems([favoriteFileLocked1, favoriteFolder2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toEqual(`2 items couldn't be deleted`); - }); + it('notification on multiple items deletion - all items fail to delete - [C280521]', async () => { + await dataTable.selectMultipleItems([favoriteFileLocked1, favoriteFolder2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toEqual(`2 items couldn't be deleted`); }); - it('successful delete notification shows Undo action - [C280522]', () => { - dataTable.selectItem(favoriteFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => expect(message).toContain(`Undo`)) + it('successful delete notification shows Undo action - [C280522]', async () => { + await dataTable.selectItem(favoriteFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Undo`); - .then(() => apis.user.trashcan.restore(favoriteFile1Id)); + await apis.user.trashcan.restore(favoriteFile1Id); }); - it('unsuccessful delete notification does not show Undo action - [C280523]', () => { - dataTable.selectItem(favoriteFolder2) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => expect(message).not.toContain(`Undo`)); + it('unsuccessful delete notification does not show Undo action - [C280523]', async () => { + await dataTable.selectItem(favoriteFolder2); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).not.toContain(`Undo`); }); - it('undo delete of file - [C280524]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('undo delete of file - [C280524]', async () => { + const items = await page.dataTable.countRows(); - dataTable.selectItem(favoriteFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => { - expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item was not restored'); - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }); + await dataTable.selectItem(favoriteFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item was not restored'); + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); }); - it('undo delete of folder with content - [C280526]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('undo delete of folder with content - [C280526]', async () => { + const items = await page.dataTable.countRows(); - dataTable.selectItem(favoriteFolder1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => { - expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item was not restored'); - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }) - .then(() => dataTable.doubleClickOnRowByName(favoriteFolder1)) - .then(() => expect(dataTable.getRowByName(favoriteFile3).isPresent()).toBe(true, 'file from folder not restored')); + await dataTable.selectItem(favoriteFolder1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item was not restored'); + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); + await dataTable.doubleClickOnRowByName(favoriteFolder1); + expect(dataTable.getRowByName(favoriteFile3).isPresent()).toBe(true, 'file from folder not restored'); }); - it('undo delete of multiple files - [C280525]', () => { - let items: number; - page.dataTable.countRows().then(number => { items = number; }); + it('undo delete of multiple files - [C280525]', async () => { + const items = await page.dataTable.countRows(); - dataTable.selectMultipleItems([favoriteFile1, favoriteFile2]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => { - expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} was not removed from list`); - expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} was not removed from list`); - expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); - }); + await dataTable.selectMultipleItems([favoriteFile1, favoriteFile2]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} was not removed from list`); + expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} was not removed from list`); + expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); }); }); @@ -602,119 +541,110 @@ describe('Delete and undo delete', () => { const recentFile3 = `recentFile3-${Utils.random()}.txt`; let recentFile3Id; const recentFile4 = `recentFile4-${Utils.random()}.txt`; let recentFile4Id; - beforeAll(done => { - apis.user.nodes.createFile(recentFile1).then(resp => recentFile1Id = resp.entry.id) - .then(() => apis.user.nodes.createFile(recentFile2).then(resp => recentFile2Id = resp.entry.id)) - .then(() => apis.user.nodes.createFile(recentFile3).then(resp => recentFile3Id = resp.entry.id)) - .then(() => apis.user.nodes.createFile(recentFile4).then(resp => recentFile4Id = resp.entry.id)) - .then(() => apis.user.search.waitForApi(username, { expect: 4 })) + beforeAll(async (done) => { + recentFile1Id = (await apis.user.nodes.createFile(recentFile1)).entry.id; + recentFile2Id = (await apis.user.nodes.createFile(recentFile2)).entry.id; + recentFile3Id = (await apis.user.nodes.createFile(recentFile3)).entry.id; + recentFile4Id = (await apis.user.nodes.createFile(recentFile4)).entry.id; + await apis.user.search.waitForApi(username, { expect: 4 }); - .then(() => loginPage.loginWith(username)) + await loginPage.loginWith(username); - .then((): any => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES) - .then(() => dataTable.isEmptyList()) - .then(empty => { - if (empty) { - browser.sleep(6000).then(() => page.refresh()); - } - }) - ) - .then(done); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + const empty = await dataTable.isEmptyList(); + if (empty) { + await browser.sleep(6000); + await browser.refresh(); + } + done(); }); - beforeEach(done => { - page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES) - .then(() => dataTable.waitForHeader()) - .then(done); + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await dataTable.waitForHeader(); + done(); }); - afterEach(done => { - page.refresh().then(done); + afterEach(async (done) => { + await page.refresh(); + done(); }); - afterAll(done => { - Promise.all([ + afterAll(async (done) => { + await Promise.all([ logoutPage.load(), apis.user.nodes.deleteNodesById([recentFile1Id, recentFile2Id, recentFile3Id, recentFile4Id]) - ]) - .then(done); + ]); + done(); }); - it('delete a file and check notification - [C280528]', () => { - dataTable.selectItem(recentFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`${recentFile1} deleted`); - expect(dataTable.getRowByName(recentFile1).isPresent()).toBe(false, 'Item was not removed from list'); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => expect(dataTable.getRowByName(recentFile1).isPresent()).toBe(true, 'Item is not in trash')) + xit('delete a file and check notification - [C280528]', async () => { + await dataTable.selectItem(recentFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`${recentFile1} deleted`); + expect(dataTable.getRowByName(recentFile1).isPresent()).toBe(false, 'Item was not removed from list'); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(recentFile1).isPresent()).toBe(true, 'Item is not in trash'); - .then(() => apis.user.trashcan.restore(recentFile1Id)) - .then(() => apis.user.search.waitForApi(username, { expect: 4 })); + await apis.user.trashcan.restore(recentFile1Id); + await apis.user.search.waitForApi(username, { expect: 4 }); }); - it('delete multiple files and check notification - [C280529]', () => { - dataTable.selectMultipleItems([recentFile2, recentFile3]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => { - expect(message).toContain(`Deleted 2 items`); - expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(false, `${recentFile2} was not removed from list`); - expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} was not removed from list`); - }) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(true, `${recentFile2} is not in trash`); - expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(true, `${recentFile3} is not in trash`); - }) + xit('delete multiple files and check notification - [C280529]', async () => { + await dataTable.selectMultipleItems([recentFile2, recentFile3]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Deleted 2 items`); + expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(false, `${recentFile2} was not removed from list`); + expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} was not removed from list`); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(true, `${recentFile2} is not in trash`); + expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(true, `${recentFile3} is not in trash`); - .then(() => apis.user.trashcan.restore(recentFile2Id)) - .then(() => apis.user.trashcan.restore(recentFile3Id)) - .then(() => apis.user.search.waitForApi(username, { expect: 4 })); + await apis.user.trashcan.restore(recentFile2Id); + await apis.user.trashcan.restore(recentFile3Id); + await apis.user.search.waitForApi(username, { expect: 4 }); }); - it('successful delete notification shows Undo action - [C280534]', () => { - dataTable.selectItem(recentFile1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.getSnackBarMessage()) - .then(message => expect(message).toContain(`Undo`)) + xit('successful delete notification shows Undo action - [C280534]', async () => { + await dataTable.selectItem(recentFile1); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + const message = await page.getSnackBarMessage(); + expect(message).toContain(`Undo`); - .then(() => apis.user.trashcan.restore(recentFile1Id)) - .then(() => apis.user.search.waitForApi(username, { expect: 4 })); + await apis.user.trashcan.restore(recentFile1Id); + await apis.user.search.waitForApi(username, { expect: 4 }); }); // due to the fact that the search api is slow to update, // we cannot test that the restored file is displayed in the Recent Files list // without adding a very big browser.sleep followed by a page.refresh // so for the moment we're testing that the restored file is not displayed in the Trash - it('undo delete of file - [C280536]', () => { - dataTable.selectItem(recentFile2) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(false, 'Item is in Trash')); + xit('undo delete of file - [C280536]', async () => { + await dataTable.selectItem(recentFile2); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(false, 'Item is in Trash'); }); // due to the fact that the search api is slow to update, // we cannot test that the restored file is displayed in the Recent Files list // without adding a very big browser.sleep followed by a page.refresh // so for the moment we're testing that the restored file is not displayed in the Trash - it('undo delete of multiple files - [C280537]', () => { - dataTable.selectMultipleItems([recentFile3, recentFile4]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Delete')) - .then(() => page.clickSnackBarAction()) - .then(() => page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) - .then(() => { - expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} is in Trash`); - expect(dataTable.getRowByName(recentFile4).isPresent()).toBe(false, `${recentFile4} is in Trash`); - }); + xit('undo delete of multiple files - [C280537]', async () => { + await dataTable.selectMultipleItems([recentFile3, recentFile4]); + await toolbar.actions.openMoreMenu(); + await toolbar.actions.menu.clickMenuItem('Delete'); + await page.clickSnackBarAction(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} is in Trash`); + expect(dataTable.getRowByName(recentFile4).isPresent()).toBe(false, `${recentFile4} is in Trash`); }); }); }); From c06b6e317bd0433ad97eb1e8cff0bfbcfcc156d5 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Sat, 8 Sep 2018 17:36:10 +0100 Subject: [PATCH 005/160] run protractor directly --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1d5081c30..2dc2c8044 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,10 @@ "test:ci": "npm run build.libs.dev && ng test app --code-coverage --watch=false", "lint": "ng lint", "server-versions": "rimraf ./src/versions.json && npm list --depth=0 --json=true --prod=true > ./src/versions.json || exit 0", - "e2e": "ng e2e", - "wd:update": "webdriver-manager update --gecko=false --versions.chrome=2.38", - "_e2e": "npm run wd:update && protractor protractor.conf.js", + "__e2e": "ng e2e", + "__wd:update": "webdriver-manager update --gecko=false --versions.chrome=2.38", + "wd:update": "webdriver-manager update --gecko=false", + "e2e": "npm run wd:update && protractor protractor.conf.js", "start:docker": "docker-compose up -d --build && wait-on http://localhost:8080 && wait-on http://localhost:4000", "stop:docker": "docker-compose stop", "e2e:docker": "npm run start:docker && npm run e2e && npm run stop:docker", From c166f255b5fde9e29ba9c1079d4aef540c02f411 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Tue, 11 Sep 2018 21:30:26 +0100 Subject: [PATCH 006/160] extension guide and example (#623) * extension guide and example * code and guide fixes * update dependencies --- angular.json | 40 ++++ cspell.json | 1 + docs/extending.md | 226 ++++++++++++++++++ package-lock.json | 131 +++++----- package.json | 14 +- .../my-extension/assets/my-extension.json | 30 +++ projects/my-extension/karma.conf.js | 31 +++ projects/my-extension/ng-package.json | 7 + projects/my-extension/package.json | 8 + .../src/lib/my-extension.component.spec.ts | 25 ++ .../src/lib/my-extension.component.ts | 19 ++ .../src/lib/my-extension.module.ts | 18 ++ .../src/lib/my-extension.service.spec.ts | 12 + .../src/lib/my-extension.service.ts | 9 + projects/my-extension/src/public_api.ts | 7 + projects/my-extension/src/test.ts | 22 ++ projects/my-extension/tsconfig.lib.json | 32 +++ projects/my-extension/tsconfig.spec.json | 17 ++ projects/my-extension/tslint.json | 17 ++ src/app/extensions.module.ts | 4 +- src/assets/app.extensions.json | 3 +- tsconfig.json | 8 +- 22 files changed, 616 insertions(+), 65 deletions(-) create mode 100644 projects/my-extension/assets/my-extension.json create mode 100644 projects/my-extension/karma.conf.js create mode 100644 projects/my-extension/ng-package.json create mode 100644 projects/my-extension/package.json create mode 100644 projects/my-extension/src/lib/my-extension.component.spec.ts create mode 100644 projects/my-extension/src/lib/my-extension.component.ts create mode 100644 projects/my-extension/src/lib/my-extension.module.ts create mode 100644 projects/my-extension/src/lib/my-extension.service.spec.ts create mode 100644 projects/my-extension/src/lib/my-extension.service.ts create mode 100644 projects/my-extension/src/public_api.ts create mode 100644 projects/my-extension/src/test.ts create mode 100644 projects/my-extension/tsconfig.lib.json create mode 100644 projects/my-extension/tsconfig.spec.json create mode 100644 projects/my-extension/tslint.json diff --git a/angular.json b/angular.json index 42c0aba18..efbbedfcf 100644 --- a/angular.json +++ b/angular.json @@ -55,6 +55,11 @@ "glob": "**/*.js", "input": "node_modules/@ngstack/code-editor/workers", "output": "./assets/workers" + }, + { + "glob": "**/*.json", + "input": "dist/my-extension/assets", + "output": "/assets/plugins" } ], "styles": [ @@ -284,6 +289,41 @@ } } } + }, + "my-extension": { + "root": "projects/my-extension", + "sourceRoot": "projects/my-extension/src", + "projectType": "library", + "prefix": "lib", + "targets": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/my-extension/tsconfig.lib.json", + "project": "projects/my-extension/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/my-extension/src/test.ts", + "tsConfig": "projects/my-extension/tsconfig.spec.json", + "karmaConfig": "projects/my-extension/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/my-extension/tsconfig.lib.json", + "projects/my-extension/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } } }, "defaultProject": "app", diff --git a/cspell.json b/cspell.json index ddb8d9a9e..64c56caf6 100644 --- a/cspell.json +++ b/cspell.json @@ -4,6 +4,7 @@ "words": [ "succes", "sharedlinks", + "Redistributable", "ngrx", "ngstack", diff --git a/docs/extending.md b/docs/extending.md index d91d72938..9f07fcb2d 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -1567,3 +1567,229 @@ node: 10 You have successfully created a new menu button that invokes your custom action and redirects you to the extra application route. + +## Redistributable libraries + +Extension libraries are based on the standard Angular libraries and definition files in the JSON format. + +

+Please read more details in the following article: [Library support in Angular CLI 6](https://github.com/angular/angular-cli/wiki/stories-create-library#library-support-in-angular-cli-6) +

+ +### Creating extension library + +First, generate a new project within the workspace: + +```sh +ng generate library my-extension +``` + +You will get a new project in the `projects/my-extensions` folder. +By default, the project contains at least the following content: + +* Example component `my-extension.component.ts` +* Example service `my-extension.service.ts` +* Angular Module example `my-extension.module.ts` + +Next, build the project with the following command: + +```sh +ng build my-extension +``` + +Angular CLI automatically configures Typescript path mappings for the project, +so that you do not need any additional steps to link the library. + +#### Register dynamic components + +Update `my-extension.module.ts` and put all the content you plan to use at runtime dynamically to the `entryComponents` section of the module. + +```typescript +@NgModule({ + imports: [], + declarations: [MyExtensionComponent], + exports: [MyExtensionComponent], + entryComponents: [MyExtensionComponent] +}) +export class MyExtensionModule { } +``` + +Let's now register `MyExtensionComponent` as an extension component. +Update the code as in the next example: + +```typescript +import { ExtensionService } from '@alfresco/adf-extensions'; + +@NgModule({...}) +export class MyExtensionModule { + constructor(extensions: ExtensionService) { + extensions.setComponents({ + 'my-extension.main.component': MyExtensionComponent, + }); + } +} +``` + +Now you can use `my-extension.main.component` identifier in the JSON definitions +if you want to reference the `MyExtensionComponent`. + +#### Plugin definition file + +Create a new `assets/my-extension.json` file in the library project root folder with the following content: + +```json +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + "$description": "demo plugin", + + "routes": [ + { + "id": "my.extension.route", + "path": "ext/my/route", + "component": "my-extension.main.component" + } + ], + + "features": { + "navbar": [ + { + "id": "my.extension.nav", + "items": [ + { + "id": "my.extension.main", + "icon": "extension", + "title": "My Extension", + "route": "my.extension.route" + } + ] + } + ] + } +} +``` + +Update the root `package.json` file and append the following entry to the `scripts` section: + +```json +{ + "scripts": { + ..., + + "build:my-extension": + "ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst" + } +} +``` + +You can now use that script to build the library and copy assets to the output folder. + +

+It is a good practice to provide installation instructions for your library in the `README.md` file. +Be sure to mention that developers should have a build rule to copy your plugin definition file to the `assets/plugins` folder of the main application. +

+ +### Publishing library to NPM + +You should rebuild the library every time before publishing: + +```sh +npm run build:my-extension +``` + +Go to the output folder and run the publish command. + +```sh +cd dist/my-extension +npm publish +``` + +Note that requires for you have a valid [NPM](https://www.npmjs.com/) account. + +

+See more details in the [Publishing your library](https://github.com/angular/angular-cli/wiki/stories-create-library#publishing-your-library) article. +

+ +### Consuming extension library + +Assuming that you have published your extension library to NPM, +you can install it using the standard command: + +```sh +npm install my-extension +``` + +That should install the library and all its dependencies. + +

+You do not need installing library in the original workspace as the application is +already configured to use the local version from the `dist` folder. +

+ +#### Copy assets + +Edit `angular.json` configuration file and add the following rule if you develop and test extension library in the same workspace + +```json +{ + "glob": "**/*.json", + "input": "dist/my-extension/assets", + "output": "/assets/plugins" +} +``` + +Use the following rule in case you are installing extension from NPM: + +```json +{ + "glob": "**/*.json", + "input": "node_modules/my-extension/assets", + "output": "/assets/plugins" +} +``` + +#### Register module + +In the main application, edit the `src/app/extensions.module.ts` file and append the module declaration as in the next example: + +```typescript +... +import { MyExtensionModule } from 'my-extension'; + +@NgModule({ + ... + imports: [ + ..., + MyExtensionModule + ] +}) +export class AppExtensionsModule {} +``` + +#### Register plugin + +Finally, update the `assets/app.extensions.json` file and add a reference to the new plugin: + +```json +{ + "$references": [ + ..., + "my-extension.json" + ] +} +``` + +### Testing library + +Run the application and ensure you have an extra navigation sidebar entry: + +```sh +npm start +``` + +Click the `My Extension` link and in the main content area you should see the extension component coming from your library. + +

+Depending on the application setup, you may need enabling external plugins via the `Settings` dialog available for `admin` users (clicking the application profile button). +

diff --git a/package-lock.json b/package-lock.json index 05f8afe20..b6c181d70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -622,19 +622,19 @@ } }, "@angular-devkit/schematics": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.0.tgz", - "integrity": "sha512-XmyNyj/JDXXKACH5XE6cGht6YH5n8KpVLr/X3EPp322yB8HlWWDOsI1UwchzNU769aP7YwjDNwEmu3+8RGD6Ng==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.1.tgz", + "integrity": "sha512-ab3xeyTpPA9JO7oXgdfbQ/+5djddvoKjFaxFFcLD5GxgDDnvJcmhgDkluJY9JZHH2oSFaW8u1G5zg8uo1HrriA==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.0", + "@angular-devkit/core": "0.8.1", "rxjs": "~6.2.0" }, "dependencies": { "@angular-devkit/core": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", - "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", "dev": true, "requires": { "ajv": "~6.4.0", @@ -1054,16 +1054,16 @@ } }, "@angular/cli": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.0.tgz", - "integrity": "sha512-F0BfB59e7HM/STneWj5VvGZHDfotFvY9KKLzJgsazaJaDxo2sPDHm4gTJSHH9FgfuVQkA5e4N11uMeI110zzsA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.1.tgz", + "integrity": "sha512-4AO014PohYc/vbNaO6nPi/a6JqxdOHN2m0WLutgRGoBQswqSGn7aLEG1erZKRzbfq39E1a/efnNmEI3Okl3h1Q==", "dev": true, "requires": { - "@angular-devkit/architect": "0.8.0", - "@angular-devkit/core": "0.8.0", - "@angular-devkit/schematics": "0.8.0", - "@schematics/angular": "0.8.0", - "@schematics/update": "0.8.0", + "@angular-devkit/architect": "0.8.1", + "@angular-devkit/core": "0.8.1", + "@angular-devkit/schematics": "0.8.1", + "@schematics/angular": "0.8.1", + "@schematics/update": "0.8.1", "json-schema-traverse": "^0.4.1", "opn": "^5.3.0", "rxjs": "~6.2.0", @@ -1073,19 +1073,19 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.0.tgz", - "integrity": "sha512-rgyucHNPzJawBYqM9LSnOWNm9KM6E8ovzvQgzF8SePderqn6TGFAlrDvaZasgLtrihCAR//lNhKUm8w8ft3rgQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.1.tgz", + "integrity": "sha512-bf/8tg8X2y9f6wE2r48KAW2AVexfGd/rfTHRvl9+kSsFFtXVA233GNAL6Qs+wJ/G2t1NFddnE3ME2eyhJYxBwA==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.0", + "@angular-devkit/core": "0.8.1", "rxjs": "~6.2.0" } }, "@angular-devkit/core": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", - "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", "dev": true, "requires": { "ajv": "~6.4.0", @@ -1695,37 +1695,26 @@ } }, "@schematics/angular": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.0.tgz", - "integrity": "sha512-D7fCtbhHg4aHRyDfSFxjKbcMOgz9IzMBWVjHJU7LGCGvX67H6eCNqbEf/Ogu1jzFQjMFR5VvPjXhamqTXjv7ug==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.1.tgz", + "integrity": "sha512-AW7063IaYFIcskt+eI5k4drb/hDuY2wK3zLsW01E49WaBcRhXVIOVox7cbfwHCA6zch117WZ5xVZlbGHJ4pkMw==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.0", - "@angular-devkit/schematics": "0.8.0", + "@angular-devkit/core": "0.8.1", + "@angular-devkit/schematics": "0.8.1", "typescript": ">=2.6.2 <2.10" }, "dependencies": { "@angular-devkit/core": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", - "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", "dev": true, "requires": { "ajv": "~6.4.0", "chokidar": "^2.0.3", "rxjs": "~6.2.0", "source-map": "^0.5.6" - }, - "dependencies": { - "rxjs": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", - "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } } }, "ajv": { @@ -2102,6 +2091,15 @@ "to-regex": "^3.0.2" } }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "uri-js": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", @@ -2114,13 +2112,13 @@ } }, "@schematics/update": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.0.tgz", - "integrity": "sha512-5nrvUWdsrn/iW/V4Dk0a1/yxnzmstxVlsI0PKFRImkU67pVmJDGkELqFg1fQBnqKcFJoBUHtkGt3+P4b8BdHsw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.1.tgz", + "integrity": "sha512-G5EU8nvqAC9fX09sV+UEM9EgR+PjGwguUatOk10uvwvZvCfO+W0FVeLmSap9A6mJ+e8YOH6XFpkwLG0AMuhYrw==", "dev": true, "requires": { - "@angular-devkit/core": "0.8.0", - "@angular-devkit/schematics": "0.8.0", + "@angular-devkit/core": "0.8.1", + "@angular-devkit/schematics": "0.8.1", "npm-registry-client": "^8.5.1", "rxjs": "~6.2.0", "semver": "^5.3.0", @@ -2128,9 +2126,9 @@ }, "dependencies": { "@angular-devkit/core": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.0.tgz", - "integrity": "sha512-2kzpw4CHeZOrbs2iKeEmqQuj4tfqMbklrVXt2ktQmasTc83pgEaXbUvnxb1He+UtSkuVq3ErZ3ZRlDf/fmeRZQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", "dev": true, "requires": { "ajv": "~6.4.0", @@ -2575,7 +2573,8 @@ "@types/showdown": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-1.7.5.tgz", - "integrity": "sha512-uUSUP6XtyTclRzTH0NLkEIiEowxYXOWDeulpngrPltEceOmsGdhfrl8xr3D4QfJA7FuUUyHwFQuWWURLFg3hgg==" + "integrity": "sha512-uUSUP6XtyTclRzTH0NLkEIiEowxYXOWDeulpngrPltEceOmsGdhfrl8xr3D4QfJA7FuUUyHwFQuWWURLFg3hgg==", + "dev": true }, "@types/strip-bom": { "version": "3.0.0", @@ -2866,9 +2865,9 @@ } }, "alfresco-js-api-node": { - "version": "2.5.0-b048ff2aad00611aac1c02af531549b60acc0e66", - "resolved": "https://registry.npmjs.org/alfresco-js-api-node/-/alfresco-js-api-node-2.5.0-b048ff2aad00611aac1c02af531549b60acc0e66.tgz", - "integrity": "sha512-HuRpoyuDYRF+IcuAApEJNeZWgfvW0ioe+QXiPwsGlHzgbYlqz2iwGPuXBaxp4XdfL0g2IIckf2MElp6IBpFbQA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/alfresco-js-api-node/-/alfresco-js-api-node-2.5.0.tgz", + "integrity": "sha512-sx/tBSaY24mkSz9yLwVEWGHDP+BSQT6JfrjdxDgxecop27bYC/EjZCFlS/lCDKXXeeJtGrEL59CQZmJ+NTDGlw==", "dev": true, "requires": { "event-emitter": "0.3.4", @@ -4130,9 +4129,9 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "codelyzer": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.4.3.tgz", - "integrity": "sha512-BrARi+8uo3gajjFFXA5bMhE0wZ0nd/QRjyLISA37anjN2f7pxcb6rkhQX9/GJLTwsUiSaOLbghSIWkHy4F67NQ==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.4.4.tgz", + "integrity": "sha512-JgFMudx0n50IuE/ydAfnkksCwQkWSVWgYvhDPHZgDUbmsiYC22VuEXKu5l8Hhx9UJsLgjWDLjTAFGj2WaW5DUA==", "dev": true, "requires": { "app-root-path": "^2.1.0", @@ -4504,6 +4503,26 @@ } } }, + "cpr": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz", + "integrity": "sha1-uaVQOLfNgaNcF7l2GJW9hJau8eU=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.5", + "minimist": "^1.2.0", + "mkdirp": "~0.5.1", + "rimraf": "^2.5.4" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", diff --git a/package.json b/package.json index 2dc2c8044..cae435f7a 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "ng": "ng", "start": "npm run server-versions && npm run build.libs.dev && ng serve --open", "start:prod": "npm run server-versions && npm run build.libs.prod && ng serve --prod --open", - "build.libs.dev": "ng build adf-extensions && ng build aca-dev-tools", - "build.libs.prod": "ng build adf-extensions --prod && ng build aca-dev-tools --prod", + "build.libs.dev": "ng build adf-extensions && ng build aca-dev-tools && npm run build:my-extension", + "build.libs.prod": "ng build adf-extensions --prod && ng build aca-dev-tools --prod && npm run build:my-extension", "build": "npm run server-versions && npm run build.libs.prod && node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod", "build:dev": "npm run server-versions && npm run build.libs.dev && ng build", + "build:my-extension": "ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst", "test": "npm run build.libs.dev && ng test app --code-coverage", "test:ci": "npm run build.libs.dev && ng test app --code-coverage --watch=false", "lint": "ng lint", @@ -49,7 +50,6 @@ "@ngrx/store-devtools": "^6.1.0", "@ngstack/code-editor": "^0.4.3", "@ngx-translate/core": "^10.0.2", - "@types/showdown": "^1.7.5", "alfresco-js-api": "2.5.0", "core-js": "^2.5.7", "hammerjs": "2.0.8", @@ -66,15 +66,17 @@ "devDependencies": { "@angular-devkit/build-angular": "~0.8.0", "@angular-devkit/build-ng-packagr": "~0.8.0", - "@angular/cli": "^6.2.0", + "@angular/cli": "^6.2.1", "@angular/compiler-cli": "6.1.7", "@angular/language-service": "6.1.7", "@types/jasmine": "^2.5.53", "@types/jasminewd2": "^2.0.2", "@types/node": "9.3.0", "@types/selenium-webdriver": "^3.0.8", - "alfresco-js-api-node": "2.5.0-b048ff2aad00611aac1c02af531549b60acc0e66", - "codelyzer": "^4.4.3", + "@types/showdown": "^1.7.5", + "alfresco-js-api-node": "^2.5.0", + "codelyzer": "^4.4.4", + "cpr": "^3.0.1", "cspell": "^3.1.3", "jasmine-core": "~2.8.0", "jasmine-reporters": "^2.2.1", diff --git a/projects/my-extension/assets/my-extension.json b/projects/my-extension/assets/my-extension.json new file mode 100644 index 000000000..3e492a708 --- /dev/null +++ b/projects/my-extension/assets/my-extension.json @@ -0,0 +1,30 @@ +{ + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + "$description": "demo plugin", + + "routes": [ + { + "id": "my.extension.route", + "path": "ext/my/route", + "component": "my-extension.main.component" + } + ], + + "features": { + "navbar": [ + { + "id": "my.extension.nav", + "items": [ + { + "id": "my.extension.main", + "icon": "extension", + "title": "My Extension", + "route": "my.extension.route" + } + ] + } + ] + } +} diff --git a/projects/my-extension/karma.conf.js b/projects/my-extension/karma.conf.js new file mode 100644 index 000000000..4c5f8d03f --- /dev/null +++ b/projects/my-extension/karma.conf.js @@ -0,0 +1,31 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage'), + reports: ['html', 'lcovonly'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; diff --git a/projects/my-extension/ng-package.json b/projects/my-extension/ng-package.json new file mode 100644 index 000000000..ed4d8c2eb --- /dev/null +++ b/projects/my-extension/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/my-extension", + "lib": { + "entryFile": "src/public_api.ts" + } +} \ No newline at end of file diff --git a/projects/my-extension/package.json b/projects/my-extension/package.json new file mode 100644 index 000000000..261671ec8 --- /dev/null +++ b/projects/my-extension/package.json @@ -0,0 +1,8 @@ +{ + "name": "my-extension", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^6.0.0-rc.0 || ^6.0.0", + "@angular/core": "^6.0.0-rc.0 || ^6.0.0" + } +} \ No newline at end of file diff --git a/projects/my-extension/src/lib/my-extension.component.spec.ts b/projects/my-extension/src/lib/my-extension.component.spec.ts new file mode 100644 index 000000000..349674994 --- /dev/null +++ b/projects/my-extension/src/lib/my-extension.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyExtensionComponent } from './my-extension.component'; + +describe('MyExtensionComponent', () => { + let component: MyExtensionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MyExtensionComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MyExtensionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/my-extension/src/lib/my-extension.component.ts b/projects/my-extension/src/lib/my-extension.component.ts new file mode 100644 index 000000000..64ac67f20 --- /dev/null +++ b/projects/my-extension/src/lib/my-extension.component.ts @@ -0,0 +1,19 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'lib-my-extension', + template: ` +

+ my-extension works! +

+ `, + styles: [] +}) +export class MyExtensionComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/projects/my-extension/src/lib/my-extension.module.ts b/projects/my-extension/src/lib/my-extension.module.ts new file mode 100644 index 000000000..ff56075f2 --- /dev/null +++ b/projects/my-extension/src/lib/my-extension.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { MyExtensionComponent } from './my-extension.component'; +import { ExtensionService } from '@alfresco/adf-extensions'; + +@NgModule({ + imports: [ + ], + declarations: [MyExtensionComponent], + exports: [MyExtensionComponent], + entryComponents: [MyExtensionComponent] +}) +export class MyExtensionModule { + constructor(extensions: ExtensionService) { + extensions.setComponents({ + 'my-extension.main.component': MyExtensionComponent, + }); + } +} diff --git a/projects/my-extension/src/lib/my-extension.service.spec.ts b/projects/my-extension/src/lib/my-extension.service.spec.ts new file mode 100644 index 000000000..02efff0f8 --- /dev/null +++ b/projects/my-extension/src/lib/my-extension.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { MyExtensionService } from './my-extension.service'; + +describe('MyExtensionService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: MyExtensionService = TestBed.get(MyExtensionService); + expect(service).toBeTruthy(); + }); +}); diff --git a/projects/my-extension/src/lib/my-extension.service.ts b/projects/my-extension/src/lib/my-extension.service.ts new file mode 100644 index 000000000..d67ac35e5 --- /dev/null +++ b/projects/my-extension/src/lib/my-extension.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class MyExtensionService { + + constructor() { } +} diff --git a/projects/my-extension/src/public_api.ts b/projects/my-extension/src/public_api.ts new file mode 100644 index 000000000..e53c40142 --- /dev/null +++ b/projects/my-extension/src/public_api.ts @@ -0,0 +1,7 @@ +/* + * Public API Surface of my-extension + */ + +export * from './lib/my-extension.service'; +export * from './lib/my-extension.component'; +export * from './lib/my-extension.module'; diff --git a/projects/my-extension/src/test.ts b/projects/my-extension/src/test.ts new file mode 100644 index 000000000..e11ff1c97 --- /dev/null +++ b/projects/my-extension/src/test.ts @@ -0,0 +1,22 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'core-js/es7/reflect'; +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/my-extension/tsconfig.lib.json b/projects/my-extension/tsconfig.lib.json new file mode 100644 index 000000000..89a9991c0 --- /dev/null +++ b/projects/my-extension/tsconfig.lib.json @@ -0,0 +1,32 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "module": "es2015", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "inlineSources": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "types": [], + "lib": [ + "dom", + "es2015" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/projects/my-extension/tsconfig.spec.json b/projects/my-extension/tsconfig.spec.json new file mode 100644 index 000000000..16da33db0 --- /dev/null +++ b/projects/my-extension/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/projects/my-extension/tslint.json b/projects/my-extension/tslint.json new file mode 100644 index 000000000..73f120b7e --- /dev/null +++ b/projects/my-extension/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "lib", + "camelCase" + ], + "component-selector": [ + true, + "element", + "lib", + "kebab-case" + ] + } +} diff --git a/src/app/extensions.module.ts b/src/app/extensions.module.ts index 52a22d85b..fac563865 100644 --- a/src/app/extensions.module.ts +++ b/src/app/extensions.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { AcaDevToolsModule } from 'aca-dev-tools'; import { CodeEditorModule } from '@ngstack/code-editor'; +import { MyExtensionModule } from 'my-extension'; // Main entry point for external extensions only. // For any application-specific code use CoreExtensionsModule instead. @@ -13,7 +14,8 @@ import { CodeEditorModule } from '@ngstack/code-editor'; // use local Typings Worker typingsWorkerUrl: 'assets/workers/typings-worker.js' }), - AcaDevToolsModule + AcaDevToolsModule, + MyExtensionModule ] }) export class AppExtensionsModule {} diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json index aa9c46969..60e055b4c 100644 --- a/src/assets/app.extensions.json +++ b/src/assets/app.extensions.json @@ -5,7 +5,8 @@ "$references": [ "plugin1.json", "plugin2.json", - "dev.tools.json" + "dev.tools.json", + "my-extension.json" ], "rules": [ diff --git a/tsconfig.json b/tsconfig.json index 6dca9206f..5cb010a3e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,6 +30,12 @@ ], "@alfresco/adf-extensions/*": [ "dist/@alfresco/adf-extensions/*" + ], + "my-extension": [ + "dist/my-extension" + ], + "my-extension/*": [ + "dist/my-extension/*" ] } }, @@ -39,4 +45,4 @@ "angularCompilerOptions": { "preserveWhitespaces": false } -} +} \ No newline at end of file From 1c86b4e4a4f0e843194f20ba979e826f7699e371 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Tue, 11 Sep 2018 22:47:14 +0100 Subject: [PATCH 007/160] [ACA-1803] upgrade to ADF 2.6.0 alpha (#624) --- angular.json | 10 ++++------ package-lock.json | 26 +++++++++++++------------- package.json | 8 ++++---- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/angular.json b/angular.json index efbbedfcf..845fb2db8 100644 --- a/angular.json +++ b/angular.json @@ -68,10 +68,9 @@ "src/styles.scss" ], "scripts": [ - "node_modules/pdfjs-dist/build/pdf.js", - "node_modules/pdfjs-dist/lib/shared/compatibility.js", - "node_modules/pdfjs-dist/web/pdf_viewer.js", - "node_modules/moment/min/moment.min.js" + "node_modules/pdfjs-dist/build/pdf.js", + "node_modules/pdfjs-dist/web/pdf_viewer.js", + "node_modules/moment/min/moment.min.js" ] }, "configurations": { @@ -129,7 +128,6 @@ "tsConfig": "src/tsconfig.spec.json", "scripts": [ "node_modules/pdfjs-dist/build/pdf.js", - "node_modules/pdfjs-dist/lib/shared/compatibility.js", "node_modules/pdfjs-dist/web/pdf_viewer.js", "node_modules/moment/min/moment.min.js" ], @@ -159,7 +157,7 @@ "output": "/assets" }, { - "glob": "pdf.worker.js", + "glob": "pdf.worker.min.js", "input": "node_modules/pdfjs-dist/build", "output": "/" } diff --git a/package-lock.json b/package-lock.json index b6c181d70..7fa447ee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,17 +5,17 @@ "requires": true, "dependencies": { "@alfresco/adf-content-services": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-2.5.0.tgz", - "integrity": "sha512-c2flI+A99YDP7BZ7lIWMC6JFqsWp5RC81BpZBKXBRw6R8YsJwbBwi9iZu+HZHQe6927THBMxDQX1uXT21ct7FA==", + "version": "2.6.0-d037a16d3c4215522a5f786e18ea0c8f018ecb25", + "resolved": "https://registry.npmjs.org/@alfresco/adf-content-services/-/adf-content-services-2.6.0-d037a16d3c4215522a5f786e18ea0c8f018ecb25.tgz", + "integrity": "sha512-4f0ANwvwlciNK4MPe2wNQ012/vE9XPa3uA63Cx1VvaIuqJPttJpV/hDSIt/rAA+X//SSqGOjlaqIw2NU4btA9g==", "requires": { "tslib": "^1.9.0" } }, "@alfresco/adf-core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-2.5.0.tgz", - "integrity": "sha512-ifjjT1H+dOiiFumDs8tY427HQ87VcL5wErcEkPb+hvW3DSf7czRohFyzRwCADNBEYeY4fqt/bIu35y+K2G738g==", + "version": "2.6.0-d037a16d3c4215522a5f786e18ea0c8f018ecb25", + "resolved": "https://registry.npmjs.org/@alfresco/adf-core/-/adf-core-2.6.0-d037a16d3c4215522a5f786e18ea0c8f018ecb25.tgz", + "integrity": "sha512-hPQVtmIiQXkwuJQz8HfsnZkG3frC/NU5xUXN6LMXMjJcu3yNDkuNunFaxLU4d2Z5Zb8UDAWUwgfsIeSBpD6aJA==", "requires": { "tslib": "^1.9.0" } @@ -2856,9 +2856,9 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" }, "alfresco-js-api": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.5.0.tgz", - "integrity": "sha512-0DwSpofMsyLHs3kyPwl+2JvIOUmTpPeJkBF5cIXO/T6+hst9ZYKQRQLxcoCnuk6Ng43MN14NHawtKJH6jE2wyg==", + "version": "2.6.0-c87428564306f710159976b0d740a07a94326cf1", + "resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.6.0-c87428564306f710159976b0d740a07a94326cf1.tgz", + "integrity": "sha512-OPt75wllx+vjI0RgwbVdvUa4gs+UthJUS4iVaS8PhyXhXNxEHMuv2bKGH48cSolQbNH7kaurxqi80yl8b9kw3A==", "requires": { "event-emitter": "0.3.4", "superagent": "3.8.2" @@ -11276,12 +11276,12 @@ } }, "pdfjs-dist": { - "version": "2.0.303", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.0.303.tgz", - "integrity": "sha1-jABTDyQihmA7/L/dLukXwYK51EE=", + "version": "2.0.489", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.0.489.tgz", + "integrity": "sha1-Y+VLKSqGeQpFRpfrRNQ0e4+/rSc=", "requires": { "node-ensure": "^0.0.0", - "worker-loader": "^1.1.0" + "worker-loader": "^1.1.1" } }, "performance-now": { diff --git a/package.json b/package.json index cae435f7a..8ea7562ed 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ }, "private": true, "dependencies": { - "@alfresco/adf-content-services": "2.5.0", - "@alfresco/adf-core": "2.5.0", + "@alfresco/adf-content-services": "2.6.0-d037a16d3c4215522a5f786e18ea0c8f018ecb25", + "@alfresco/adf-core": "2.6.0-d037a16d3c4215522a5f786e18ea0c8f018ecb25", "@angular/animations": "6.1.7", "@angular/cdk": "^6.4.7", "@angular/common": "6.1.7", @@ -50,14 +50,14 @@ "@ngrx/store-devtools": "^6.1.0", "@ngstack/code-editor": "^0.4.3", "@ngx-translate/core": "^10.0.2", - "alfresco-js-api": "2.5.0", + "alfresco-js-api": "2.6.0-c87428564306f710159976b0d740a07a94326cf1", "core-js": "^2.5.7", "hammerjs": "2.0.8", "minimatch-browser": "^1.0.0", "moment": "^2.22.2", "moment-es6": "1.0.0", "monaco-editor": "^0.14.2", - "pdfjs-dist": "2.0.303", + "pdfjs-dist": "^2.0.489", "rxjs": "^6.3.2", "showdown": "^1.8.6", "web-animations-js": "2.3.1", From 28a4fb7ba793cf73d740b59d419f3d4f8861e340 Mon Sep 17 00:00:00 2001 From: Adina Parpalita Date: Wed, 12 Sep 2018 12:08:20 +0300 Subject: [PATCH 008/160] [ACA-870] add tests for right click actions (#625) --- e2e/components/data-table/data-table.ts | 17 + .../context-menu-multiple-selection.test.ts | 381 +++++++++++ .../context-menu-single-selection.test.ts | 355 ++++++++++ ...cial-permissions-available-actions.test.ts | 631 ++++++++++++++++++ .../toolbar-multiple-selection.test.ts | 303 ++------- .../actions/toolbar-single-selection.test.ts | 447 ++----------- 6 files changed, 1515 insertions(+), 619 deletions(-) create mode 100755 e2e/suites/actions/context-menu-multiple-selection.test.ts create mode 100755 e2e/suites/actions/context-menu-single-selection.test.ts create mode 100755 e2e/suites/actions/special-permissions-available-actions.test.ts diff --git a/e2e/components/data-table/data-table.ts b/e2e/components/data-table/data-table.ts index d68be01f8..f459179fb 100755 --- a/e2e/components/data-table/data-table.ts +++ b/e2e/components/data-table/data-table.ts @@ -26,6 +26,7 @@ import { ElementFinder, ElementArrayFinder, promise, by, browser, ExpectedConditions as EC, protractor } from 'protractor'; import { BROWSER_WAIT_TIMEOUT } from '../../configs'; import { Component } from '../component'; +import { Menu } from '../menu/menu'; import { Utils } from '../../utilities/utils'; export class DataTable extends Component { @@ -67,6 +68,8 @@ export class DataTable extends Component { emptyListSubtitle: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyListSubtitle)); emptyListText: ElementArrayFinder = this.component.all(by.css(DataTable.selectors.emptyListText)); + menu: Menu = new Menu(); + constructor(ancestor?: ElementFinder) { super(DataTable.selectors.root, ancestor); } @@ -208,6 +211,16 @@ export class DataTable extends Component { }); } + async rightClickOnItem(itemName: string) { + const item = this.getRowFirstCell(itemName); + await browser.actions().click(item, protractor.Button.RIGHT).perform(); + } + + async rightClickOnMultipleSelection() { + const itemFromSelection = this.getSelectedRows().get(0); + await browser.actions().click(itemFromSelection, protractor.Button.RIGHT).perform(); + } + getItemLocation(name: string) { return this.getRowByName(name).element(this.locationLink); } @@ -273,4 +286,8 @@ export class DataTable extends Component { return this.getRows().all(by.cssContainingText(DataTable.selectors.cell, name)) .map(cell => cell.getText()); } + + async hasContextMenu() { + return (await this.menu.getItemsCount()) > 0; + } } diff --git a/e2e/suites/actions/context-menu-multiple-selection.test.ts b/e2e/suites/actions/context-menu-multiple-selection.test.ts new file mode 100755 index 000000000..9c1fe4810 --- /dev/null +++ b/e2e/suites/actions/context-menu-multiple-selection.test.ts @@ -0,0 +1,381 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser, protractor } from 'protractor'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('Context menu actions - multiple selection : ', () => { + const username = `user-${Utils.random()}`; + + const file1 = `file1-${Utils.random()}.txt`; let file1Id; + const file2 = `file2-${Utils.random()}.txt`; let file2Id; + + const folder1 = `folder1-${Utils.random()}`; let folder1Id; + const folder2 = `folder2-${Utils.random()}`; let folder2Id; + + const fileInTrash1 = `deletedFile1-${Utils.random()}.txt`; let fileInTrash1Id; + const fileInTrash2 = `deletedFile2-${Utils.random()}.txt`; let fileInTrash2Id; + const folderInTrash1 = `deletedFolder1-${Utils.random()}`; let folderInTrash1Id; + const folderInTrash2 = `deletedFolder2-${Utils.random()}`; let folderInTrash2Id; + + const siteName = `site-${Utils.random()}`; + const file1Site = `file1-${Utils.random()}.txt`; + const file2Site = `file2-${Utils.random()}.txt`; + const folder1Site = `folder1-${Utils.random()}`; + const folder2Site = `folder2-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + const contextMenu = dataTable.menu; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + file1Id = (await apis.user.nodes.createFile(file1)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2)).entry.id; + folder1Id = (await apis.user.nodes.createFolder(folder1)).entry.id; + folder2Id = (await apis.user.nodes.createFolder(folder2)).entry.id; + + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); + const docLibId = (await apis.user.sites.getDocLibId(siteName)); + await apis.user.nodes.createFile(file1Site, docLibId); + await apis.user.nodes.createFile(file2Site, docLibId); + await apis.user.nodes.createFolder(folder1Site, docLibId); + await apis.user.nodes.createFile(folder2Site, docLibId); + + await apis.user.shared.shareFilesByIds([ file1Id, file2Id ]); + await apis.user.shared.waitForApi({ expect: 2 }); + + await apis.user.favorites.addFavoritesByIds('file', [ file1Id, file2Id ]); + await apis.user.favorites.addFavoritesByIds('folder', [ folder1Id, folder2Id ]); + await apis.user.favorites.waitForApi({ expect: 4 }); + + fileInTrash1Id = (await apis.user.nodes.createFile(fileInTrash1)).entry.id; + fileInTrash2Id = (await apis.user.nodes.createFile(fileInTrash2)).entry.id; + folderInTrash1Id = (await apis.user.nodes.createFolder(folderInTrash1)).entry.id; + folderInTrash2Id = (await apis.user.nodes.createFolder(folderInTrash2)).entry.id; + await apis.user.nodes.deleteNodesById([ fileInTrash1Id, fileInTrash2Id, folderInTrash1Id, folderInTrash2Id ], false); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.deleteNodesById([ file1Id, file2Id, folder1Id, folder2Id ]), + apis.user.sites.deleteSite(siteName), + apis.user.trashcan.emptyTrash(), + logoutPage.load() + ]); + done(); + }); + + xit(''); + + describe('Generic tests', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + await dataTable.clearSelection(); + done(); + }); + + it('Context menu appears on right click on a multiple selection of items - [C286268]', async () => { + await dataTable.selectMultipleItems([ file1, file2 ]); + // await dataTable.rightClickOnItem(file1); + await dataTable.rightClickOnMultipleSelection(); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + }); + + it('Context menu appears when right clicking on a single item while having multiple items selected - [C286269]', async () => { + await dataTable.selectMultipleItems([ file2, folder1 ]); + await dataTable.rightClickOnItem(file1); + expect(await dataTable.hasContextMenu()).toBe(true, `Context menu is not displayed for ${file1}`); + expect(await dataTable.countSelectedRows()).toEqual(1, 'incorrect number of selected rows'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + expect(await dataTable.hasCheckMarkIcon(file1)).toBe(true, `${file1} is not selected`); + expect(await dataTable.hasCheckMarkIcon(file2)).toBe(false, `${file2} is selected`); + expect(await dataTable.hasCheckMarkIcon(folder1)).toBe(false, `${folder1} is selected`); + }); + }); + + describe('on Personal Files', () => { + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + await dataTable.clearSelection(); + done(); + }); + + it('correct actions appear when multiple files are selected - [C280661]', async () => { + await dataTable.selectMultipleItems([file1, file2]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + + it('correct actions appear when multiple folders are selected - [C280632]', async () => { + await dataTable.selectMultipleItems([folder1, folder2]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + + it('correct actions appear when both files and folders are selected - [C280631]', async () => { + await dataTable.selectMultipleItems([file1, file2, folder1, folder2]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + }); + + describe('on File Libraries', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + done(); + }); + + it('correct actions appear when multiple files are selected - [C280641]', async () => { + await dataTable.selectMultipleItems([ file1Site, file2Site ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + + it('correct actions appear when multiple folders are selected - [C280574]', async () => { + await dataTable.selectMultipleItems([ folder1Site, folder2Site ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + + it('correct actions appear when both files and folders are selected - [C280642]', async () => { + await dataTable.selectMultipleItems([ file1Site, file2Site, folder1Site, folder2Site ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + }); + + describe('on Shared Files', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('correct actions appear when multiple files are selected - [C280648]', async () => { + await dataTable.selectMultipleItems([ file1, file2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + }); + + describe('Recent Files', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('correct actions appear when multiple files are selected - [C280652]', async () => { + await dataTable.selectMultipleItems([ file1, file2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + }); + + describe('Favorites', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + done(); + }); + + it('correct actions appear when multiple files are selected - [C280656]', async () => { + await dataTable.selectMultipleItems([ file1, file2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + + it('correct actions appear when multiple folders are selected - [C280664]', async () => { + await dataTable.selectMultipleItems([ folder1, folder2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + + it('correct actions appear when both files and folders are selected - [C280657]', async () => { + await dataTable.selectMultipleItems([ file1, file2, folder1, folder2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed`); + }); + }); + + describe('Trash', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await dataTable.waitForHeader(); + done(); + }); + + it('correct actions appear when multiple files are selected - [C286273]', async () => { + await dataTable.selectMultipleItems([ fileInTrash1, fileInTrash2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('Permanently delete')).toBe(true, 'Permanently delete is not displayed'); + expect(await contextMenu.isMenuItemPresent('Restore')).toBe(true, 'Restore is not displayed'); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(false, 'Download is displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(false, `Copy is displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(false, `Favorite is displayed`); + }); + + it('correct actions appear when multiple folders are selected - [C286274]', async () => { + await dataTable.selectMultipleItems([ folderInTrash1, folderInTrash2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('Permanently delete')).toBe(true, 'Permanently delete is not displayed'); + expect(await contextMenu.isMenuItemPresent('Restore')).toBe(true, 'Restore is not displayed'); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(false, 'Download is displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(false, `Copy is displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(false, `Favorite is displayed`); + }); + + it('correct actions appear when both files and folders are selected - [C286275]', async () => { + await dataTable.selectMultipleItems([ fileInTrash1, fileInTrash2, folderInTrash1, folderInTrash2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('Permanently delete')).toBe(true, 'Permanently delete is not displayed'); + expect(await contextMenu.isMenuItemPresent('Restore')).toBe(true, 'Restore is not displayed'); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(false, 'Download is displayed'); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, 'Edit is displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(false, `Copy is displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(false, `Favorite is displayed`); + }); + }); +}); diff --git a/e2e/suites/actions/context-menu-single-selection.test.ts b/e2e/suites/actions/context-menu-single-selection.test.ts new file mode 100755 index 000000000..7d8408c3a --- /dev/null +++ b/e2e/suites/actions/context-menu-single-selection.test.ts @@ -0,0 +1,355 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser, protractor } from 'protractor'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('Context menu actions - single selection : ', () => { + const username = `user-${Utils.random()}`; + + const fileUser = `fileUser-${Utils.random()}.txt`; let fileUserId; + const folderUser = `folderUser-${Utils.random()}`; let folderUserId; + const fileInTrash = `fileForDelete-${Utils.random()}.txt`; let fileInTrashId; + const folderInTrash = `folderForDelete-${Utils.random()}`; let folderInTrashId; + + const siteName = `userSite-${Utils.random()}`; + const fileSiteUser = `fileUser-${Utils.random()}.txt`; + const folderSiteUser = `folderUser-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + const contextMenu = dataTable.menu; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + + fileUserId = (await apis.user.nodes.createFile(fileUser)).entry.id; + folderUserId = (await apis.user.nodes.createFolder(folderUser)).entry.id; + + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); + const docLibId = await apis.user.sites.getDocLibId(siteName); + await apis.user.nodes.createFile(fileSiteUser, docLibId); + await apis.user.nodes.createFolder(folderSiteUser, docLibId); + + fileInTrashId = (await apis.user.nodes.createFiles([fileInTrash])).entry.id; + folderInTrashId = (await apis.user.nodes.createFolders([ folderInTrash ])).entry.id; + await apis.user.nodes.deleteNodeById(fileInTrashId, false); + await apis.user.nodes.deleteNodeById(folderInTrashId, false); + + await apis.user.shared.shareFileById(fileUserId); + await apis.user.shared.waitForApi({ expect: 1 }); + + await apis.user.favorites.addFavoriteById('file', fileUserId); + await apis.user.favorites.addFavoriteById('folder', folderUserId); + await apis.user.favorites.waitForApi({ expect: 2 }); + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.deleteNodeById(fileUserId), + apis.user.nodes.deleteNodeById(folderUserId), + apis.user.sites.deleteSite(siteName), + apis.user.trashcan.emptyTrash(), + logoutPage.load() + ]); + done(); + }); + + xit(''); + + describe('Generic tests', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Row is marked with a check circle icon on direct right click - [C286252]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await dataTable.hasCheckMarkIcon(fileUser)).toBe(true, 'check mark missing'); + }); + + it('Context menu appears on direct right click on an item - [C286253]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + }); + + it('Context menu appears when selecting an item and then right clicking on it - [C286254]', async () => { + await dataTable.selectItem(fileUser); + await dataTable.rightClickOnItem(fileUser); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + }); + + it('Context menu appears correctly when right clicking on another item - [C284666]', async () => { + await dataTable.selectItem(fileUser); + await dataTable.rightClickOnItem(folderUser); + expect(await dataTable.hasContextMenu()).toBe(true, `Context menu is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + expect(await dataTable.hasCheckMarkIcon(folderUser)).toBe(true, `${folderUser} is not selected`); + expect(await dataTable.hasCheckMarkIcon(fileUser)).toBe(false, `${fileUser} is not selected`); + }); + + it('Context menu closes when clicking away from it - [C280619]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed'); + await page.sidenav.activeLink.click(); + expect(await dataTable.hasContextMenu()).toBe(false, 'Context menu is displayed'); + }); + + it('Context menu does not appear for a library - [C286276]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.rightClickOnItem(siteName); + expect(await dataTable.hasContextMenu()).toBe(false, 'Context menu is displayed for a site'); + }); + }); + + describe('on Personal Files', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Context menu has the correct actions for a file - [C280615]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${fileUser}`); + }); + + it('Context menu has the correct actions for a folder - [C280616]', async () => { + await dataTable.rightClickOnItem(folderUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(false, `Manage Versions is displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is displayed for ${folderUser}`); + }); + }); + + describe('File Libraries', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + done(); + }); + + it('Context menu has the correct actions for a file - [C280594]', async () => { + await dataTable.rightClickOnItem(fileSiteUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileSiteUser}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${fileSiteUser}`); + }); + + it('Context menu has the correct actions for a folder - [C280595]', async () => { + await dataTable.rightClickOnItem(folderSiteUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(true, `Edit is not displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(false, `Manage Versions displayed for ${folderSiteUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is displayed for ${folderSiteUser}`); + }); + }); + + describe('Shared Files', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Context menu has the correct actions for a file - [C280601]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + // TODO: enable this when the action is properly implemented: ACA-92 + // expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${fileUser}`); + }); + }); + + describe('Recent Files', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await dataTable.waitForHeader(); + done(); + }); + + it('Context menu has the correct actions for a file - [C280622]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${fileUser}`); + }); + }); + + describe('Favorites', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + done(); + }); + + it('Context menu has the correct actions for a file - [C280608]', async () => { + await dataTable.rightClickOnItem(fileUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${fileUser}`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${fileUser}`); + }); + + it('Context menu has the correct actions for a folder - [C280609]', async () => { + await dataTable.rightClickOnItem(folderUser); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderUser}`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(false, `Manage Versions is displayed for ${folderUser}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is displayed for ${folderUser}`); + }); + }); + + describe('Trash', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); + await dataTable.waitForHeader(); + done(); + }); + + it('Context menu has the correct actions for a file - [C286258]', async () => { + await dataTable.rightClickOnItem(fileInTrash); + expect(await contextMenu.isMenuItemPresent('Permanently delete')) + .toBe(true, `Permanently delete is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Restore')).toBe(true, `Restore is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(false, `Download is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(false, `Favorite is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(false, `Copy is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')) + .toBe(false, `Manage Versions is not displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileInTrash}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${fileInTrash}`); + }); + + it('Context menu has the correct actions for a folder - [C286259]', async () => { + await dataTable.rightClickOnItem(folderInTrash); + expect(await contextMenu.isMenuItemPresent('Permanently delete')) + .toBe(true, `Permanently delete is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Restore')).toBe(true, `Restore is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(false, `Download is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(false, `Favorite is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(false, `Copy is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')) + .toBe(false, `Manage Versions is not displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${folderInTrash}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${folderInTrash}`); + }); + }); +}); diff --git a/e2e/suites/actions/special-permissions-available-actions.test.ts b/e2e/suites/actions/special-permissions-available-actions.test.ts new file mode 100755 index 000000000..150d1c2df --- /dev/null +++ b/e2e/suites/actions/special-permissions-available-actions.test.ts @@ -0,0 +1,631 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser, protractor } from 'protractor'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('Granular permissions available actions : ', () => { + const userConsumer = `consumer-${Utils.random()}`; + const userManager = `manager-${Utils.random()}`; + + const siteName = `site-private-${Utils.random()}`; + const file1 = `file1-${Utils.random()}.txt`; let file1Id; + const file2 = `file2-${Utils.random()}.txt`; let file2Id; + const file3 = `file3-${Utils.random()}.txt`; let file3Id; + + const folder1 = `folder1-${Utils.random()}`; let folder1Id; + const folder2 = `folder2-${Utils.random()}`; let folder2Id; + + const apis = { + admin: new RepoClient(), + userConsumer: new RepoClient(userConsumer, userConsumer), + userManager: new RepoClient(userManager, userManager) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable, toolbar } = page; + const contextMenu = dataTable.menu; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username: userConsumer }); + await apis.admin.people.createUser({ username: userManager }); + + await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE); + const docLibId = await apis.admin.sites.getDocLibId(siteName); + + file1Id = (await apis.admin.nodes.createFile(file1, docLibId)).entry.id; + file2Id = (await apis.admin.nodes.createFile(file2, docLibId)).entry.id; + file3Id = (await apis.admin.nodes.createFile(file3, docLibId)).entry.id; + folder1Id = (await apis.admin.nodes.createFolder(folder1, docLibId)).entry.id; + folder2Id = (await apis.admin.nodes.createFolder(folder2, docLibId)).entry.id; + + await apis.admin.sites.addSiteMember(siteName, userManager, SITE_ROLES.SITE_MANAGER); + await apis.admin.sites.addSiteMember(siteName, userConsumer, SITE_ROLES.SITE_CONSUMER); + + await apis.admin.nodes.setGranularPermission(file3Id, false, userConsumer, SITE_ROLES.SITE_MANAGER); + + await apis.userConsumer.shared.shareFileById(file1Id); + await apis.userConsumer.shared.shareFileById(file2Id); + await apis.admin.shared.shareFileById(file3Id); + await apis.userConsumer.shared.waitForApi({ expect: 3 }); + + await apis.userConsumer.favorites.addFavoritesByIds('file', [ file1Id, file2Id, file3Id ]); + await apis.userConsumer.favorites.addFavoritesByIds('folder', [ folder1Id, folder2Id ]); + await apis.userConsumer.favorites.waitForApi({ expect: 5 }); + + await loginPage.loginWith(userConsumer); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.admin.sites.deleteSite(siteName), + logoutPage.load() + ]); + done(); + }); + + xit(''); + + describe('toolbar displays correct actions when selecting multiple files with different granular permissions', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + done(); + }); + + it('on File Libraries - [C280476]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Shared Files - [C280477]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Favorites - [C280478]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + // TODO: enable when ACA-1737 is done + // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + }); + + describe('toolbar actions appear correctly for a file - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + done(); + }); + + it('on File Libraries - [C280455]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectItem(file1); + expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Shared Files - [C280456]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await page.dataTable.waitForHeader(); + await page.dataTable.selectItem(file1); + expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + const menu = await toolbar.actions.openMoreMenu(); + + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Favorites - [C213121]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectItem(file1); + expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); + // TODO: enable when ACA-1737 is done + // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); + // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); + await toolbar.actions.closeMoreMenu(); + }); + }); + + describe('toolbar actions appear correctly for a folder - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + done(); + }); + + it('on File Libraries - [C280444]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectItem(folder1); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folder1}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); + expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${folder1}`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); + + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folder1}`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folder1}`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folder1}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folder1}`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Favorites - [C286266]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectItem(folder1); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is not displayed for ${folder1}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); + expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${folder1}`); + // TODO: enable when ACA-1737 is done + // expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folder1}`); + // TODO: enable when ACA-1737 is done + // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folder1}`); + // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folder1}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folder1}`); + await toolbar.actions.closeMoreMenu(); + }); + }); + + describe('toolbar actions appear correctly for multiple selection of files - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + done(); + }); + + it('on File Libraries - [C280464]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Shared Files - [C286284]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Favorites - [C286285]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + // TODO: enable when ACA-1737 is done + // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + }); + + describe('toolbar actions appear correctly for multiple selection of folders - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + done(); + }); + + it('on File Libraries - [C280465]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ folder1, folder2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Favorites - [C286286]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ folder1, folder2 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + // TODO: enable when ACA-1737 is done + // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + }); + + describe('toolbar actions appear correctly for when both files and folders are selected - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + done(); + }); + + it('on File Libraries - [C280466]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, folder1 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + await toolbar.actions.closeMoreMenu(); + }); + + it('on Favorites - [C286287]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, folder1 ]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + // TODO: enable when ACA-1737 is done + // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); + // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); + await toolbar.actions.closeMoreMenu(); + }); + }); + + describe('context menu actions are correct for a file - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + done(); + }); + + it('on File Libraries - [C280599]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.rightClickOnItem(file1); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${file1}`); + }); + + it('on Shared Files - [C286264]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + await dataTable.rightClickOnItem(file1); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); + // TODO: enable this when the action is properly implemented: ACA-92 + // expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${file1}`); + }); + + it('on Favorites - [C286262]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.rightClickOnItem(file1); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); + // TODO: enable when ACA-1737 is done + // expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); + // expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${file1}`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + expect(await contextMenu.isMenuItemPresent('View details')).toBe(false, `View details is displayed for ${file1}`); + }); + }); + + describe('context menu actions are correct for a folder - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + done(); + }); + + it('on File Libraries - [C280600]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.rightClickOnItem(folder1); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(false, `Manage Versions displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is displayed for ${folder1}`); + }); + + it('on Favorites - [C286263]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.rightClickOnItem(folder1); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folder1}`); + // enable when ACA-1737 is done + // expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folder1}`); + // TODO: enable when ACA-1737 is done + // expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folder1}`); + // expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folder1}`); + // TODO: enable when ACA-1794 is fixed + // expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, `View is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(false, `Manage Versions is displayed for ${folder1}`); + expect(await contextMenu.isMenuItemPresent('Share')).toBe(false, `Share is displayed for ${folder1}`); + }); + }); + + describe('context menu actions are correct for multiple selection of files - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + done(); + }); + + it('on File Libraries - [C280647]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + + it('on Shared Files - [C286283]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + + it('on Favorites - [C286280]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, file2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + // TODO: enable when ACA-1737 is done + // expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + // expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + }); + + describe('context menu actions are correct for multiple selection of folders - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + done(); + }); + + it('on File Libraries - [C280666]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ folder1, folder2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + + it('on Favorites - [C286281]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ folder1, folder2 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + // TODO: enable when ACA-1737 is done + // expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + // expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + }); + + describe('context menu actions are correct when both files and folders are selected - consumer', () => { + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); + done(); + }); + + it('on File Libraries - [C280669]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, folder1 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + + it('on Favorites - [C286282]', async () => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + await dataTable.selectMultipleItems([ file1, folder1 ]); + await dataTable.rightClickOnMultipleSelection(); + expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed'); + expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); + // TODO: enable when ACA-1737 is done + // expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); + // expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); + expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); + expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(false, `Permissions is displayed`); + }); + }); +}); diff --git a/e2e/suites/actions/toolbar-multiple-selection.test.ts b/e2e/suites/actions/toolbar-multiple-selection.test.ts index 164641b17..0e57962b0 100755 --- a/e2e/suites/actions/toolbar-multiple-selection.test.ts +++ b/e2e/suites/actions/toolbar-multiple-selection.test.ts @@ -25,48 +25,43 @@ import { browser, protractor } from 'protractor'; import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; describe('Toolbar actions - multiple selection : ', () => { - const user1 = `user-${Utils.random()}`; - const user2 = `user-${Utils.random()}`; + const username = `user-${Utils.random()}`; - const file1 = `file-${Utils.random()}.txt`; - let file1Id; - const file2 = `file-${Utils.random()}.txt`; - let file2Id; + const file1 = `file-${Utils.random()}.txt`; let file1Id; + const file2 = `file-${Utils.random()}.txt`; let file2Id; - const folder1 = `folder-${Utils.random()}`; - let folder1Id; - const folder2 = `folder-${Utils.random()}`; - let folder2Id; + const folder1 = `folder-${Utils.random()}`; let folder1Id; + const folder2 = `folder-${Utils.random()}`; let folder2Id; const fileForDelete1 = `file-${Utils.random()}.txt`; let fileForDelete1Id; const fileForDelete2 = `file-${Utils.random()}.txt`; let fileForDelete2Id; const folderForDelete1 = `folder-${Utils.random()}`; let folderForDelete1Id; const folderForDelete2 = `folder-${Utils.random()}`; let folderForDelete2Id; - const siteName = `site-private-${Utils.random()}`; - const file1Admin = `file-${Utils.random()}.txt`; - const file2Admin = `file-${Utils.random()}.txt`; - const folder1Admin = `folder-${Utils.random()}`; - const folder2Admin = `folder-${Utils.random()}`; + const siteName = `site-${Utils.random()}`; + const file1InSite = `file1-${Utils.random()}.txt`; + const file2InSite = `file2-${Utils.random()}.txt`; + const folder1InSite = `folder1-${Utils.random()}`; + const folder2InSite = `folder2-${Utils.random()}`; const apis = { admin: new RepoClient(), - user: new RepoClient(user1, user1) + user: new RepoClient(username, username) }; const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const page = new BrowsingPage(); - const { dataTable } = page; - const { toolbar } = page; + const { dataTable, toolbar } = page; beforeAll(async (done) => { - await apis.admin.people.createUser({ username: user1 }); + await apis.admin.people.createUser({ username }); + file1Id = (await apis.user.nodes.createFiles([ file1 ])).entry.id; file2Id = (await apis.user.nodes.createFiles([ file2 ])).entry.id; folder1Id = (await apis.user.nodes.createFolders([ folder1 ])).entry.id; @@ -85,6 +80,14 @@ describe('Toolbar actions - multiple selection : ', () => { await apis.user.nodes.deleteNodesById([ fileForDelete1Id, fileForDelete2Id, folderForDelete1Id, folderForDelete2Id ], false); + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE); + const docLibId = await apis.user.sites.getDocLibId(siteName); + await apis.user.nodes.createFile(file1InSite, docLibId); + await apis.user.nodes.createFile(file2InSite, docLibId); + await apis.user.nodes.createFolder(folder1InSite, docLibId); + await apis.user.nodes.createFolder(folder2InSite, docLibId); + + await loginPage.loginWith(username); done(); }); @@ -92,6 +95,7 @@ describe('Toolbar actions - multiple selection : ', () => { await Promise.all([ apis.user.nodes.deleteNodesById([ file1Id, file2Id, folder1Id, folder2Id ]), apis.user.trashcan.emptyTrash(), + apis.user.sites.deleteSite(siteName), logoutPage.load() ]); done(); @@ -100,28 +104,18 @@ describe('Toolbar actions - multiple selection : ', () => { xit(''); describe('Personal Files', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user1); - done(); - }); - beforeEach(async (done) => { + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('Unselect items with single click - [C280458]', async () => { await dataTable.selectMultipleItems([ file1, file2, folder1, folder2 ]); expect(await dataTable.countSelectedRows()).toEqual(4, 'incorrect selected rows number'); await dataTable.selectItem(file1); expect(await dataTable.countSelectedRows()).toEqual(1, 'incorrect selected rows number'); - await dataTable.clearSelection(); }); it('Select / unselect selected items by CMD+click - [C217110]', async () => { @@ -137,7 +131,6 @@ describe('Toolbar actions - multiple selection : ', () => { await dataTable.selectItem(file2); await browser.actions().sendKeys(protractor.Key.NULL).perform(); expect(await dataTable.countSelectedRows()).toEqual(2, 'incorrect selected rows number'); - await dataTable.clearSelection(); }); it('correct actions appear when multiple files are selected - [C217112]', async () => { @@ -150,8 +143,7 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - await dataTable.clearSelection(); + await toolbar.actions.closeMoreMenu(); }); it('correct actions appear when multiple folders are selected - [C280459]', async () => { @@ -164,8 +156,7 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - await dataTable.clearSelection(); + await toolbar.actions.closeMoreMenu(); }); it('correct actions appear when both files and folders are selected - [C280460]', async () => { @@ -178,23 +169,14 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - await dataTable.clearSelection(); + await toolbar.actions.closeMoreMenu(); }); }); describe('File Libraries', () => { - beforeAll(async (done) => { - await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); - await apis.admin.people.createUser({ username: user2 }); - await apis.admin.sites.addSiteMember(siteName, user1, SITE_ROLES.SITE_MANAGER); - await apis.admin.sites.addSiteMember(siteName, user2, SITE_ROLES.SITE_CONSUMER); - await apis.admin.nodes.createFiles([ file1Admin, file2Admin ], `Sites/${siteName}/documentLibrary`); - await apis.admin.nodes.createFolders([ folder1Admin, folder2Admin ], `Sites/${siteName}/documentLibrary`); - done(); - }); - beforeEach(async (done) => { + await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); await dataTable.waitForHeader(); await dataTable.doubleClickOnRowByName(siteName); @@ -202,145 +184,52 @@ describe('Toolbar actions - multiple selection : ', () => { done(); }); - afterAll(async (done) => { - await apis.admin.sites.deleteSite(siteName); - done(); + it('correct actions appear when multiple files are selected - [C280461]', async () => { + await dataTable.selectMultipleItems([file1InSite, file2InSite]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed for selected files'); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); }); - xit(''); - - describe('user is Manager', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user1); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - - it('correct actions appear when multiple files are selected - [C280461]', async () => { - await dataTable.selectMultipleItems([file1Admin, file2Admin]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); - }); - - it('correct actions appear when multiple folders are selected - [C280462]', async () => { - await dataTable.selectMultipleItems([folder1Admin, folder2Admin]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); - }); - - it('correct actions appear when both files and folders are selected - [C280463]', async () => { - await dataTable.selectMultipleItems([file1Admin, file2Admin, folder1Admin, folder2Admin]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); - }); + it('correct actions appear when multiple folders are selected - [C280462]', async () => { + await dataTable.selectMultipleItems([folder1InSite, folder2InSite]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); }); - describe('user is Consumer', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user2); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - - it('correct actions appear when multiple files are selected - [C280464]', async () => { - await dataTable.selectMultipleItems([file1Admin, file2Admin]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); - }); - - it('correct actions appear when multiple folders are selected - [C280465]', async () => { - await dataTable.selectMultipleItems([folder1Admin, folder2Admin]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); - }); - - it('correct actions appear when both files and folders are selected - [C280466]', async () => { - await dataTable.selectMultipleItems([file1Admin, file2Admin, folder1Admin, folder2Admin]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); - }); + it('correct actions appear when both files and folders are selected - [C280463]', async () => { + await dataTable.selectMultipleItems([file1InSite, file2InSite, folder1InSite, folder2InSite]); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); }); }); describe('Shared Files', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user1); - done(); - }); - beforeEach(async (done) => { + await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('correct actions appear when multiple files are selected - [C280467]', async () => { await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); @@ -351,29 +240,18 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); }); }); describe('Recent Files', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user1); - done(); - }); - beforeEach(async (done) => { + await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('correct actions appear when multiple files are selected - [C280468]', async () => { await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); @@ -384,29 +262,18 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); }); }); describe('Favorites', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user1); - done(); - }); - beforeEach(async (done) => { + await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('correct actions appear when multiple files are selected - [C280469]', async () => { await dataTable.selectMultipleItems([file1, file2]); expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); @@ -417,9 +284,6 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); }); it('correct actions appear when multiple folders are selected - [C280470]', async () => { @@ -432,9 +296,6 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); }); it('correct actions appear when both files and folders are selected - [C280471]', async () => { @@ -447,51 +308,33 @@ describe('Toolbar actions - multiple selection : ', () => { expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - - await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform(); - await dataTable.clearSelection(); }); }); describe('Trash', () => { - beforeAll(async (done) => { - await loginPage.loginWith(user1); - done(); - }); - beforeEach(async (done) => { + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('correct actions appear when multiple files are selected - [C280472]', async () => { await dataTable.selectMultipleItems([fileForDelete1, fileForDelete2]); - expect(await toolbar.actions.isButtonPresent('Permanently delete')) - .toBe(true, 'Permanently delete is displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed for selected files'); - await dataTable.clearSelection(); + expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); + expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); }); it('correct actions appear when multiple folders are selected - [C280473]', async () => { await dataTable.selectMultipleItems([folderForDelete1, folderForDelete2]); - expect(await toolbar.actions.isButtonPresent('Permanently delete')) - .toBe(true, 'Permanently delete is displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed for selected files'); - await dataTable.clearSelection(); + expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); + expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); }); it('correct actions appear when both files and folders are selected - [C280474]', async () => { await dataTable.selectMultipleItems([fileForDelete1, fileForDelete2, folderForDelete1, folderForDelete2]); - expect(await toolbar.actions.isButtonPresent('Permanently delete')) - .toBe(true, 'Permanently delete is displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed for selected files'); - await dataTable.clearSelection(); + expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); + expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); }); }); }); diff --git a/e2e/suites/actions/toolbar-single-selection.test.ts b/e2e/suites/actions/toolbar-single-selection.test.ts index ad3871f47..4f3c2de25 100755 --- a/e2e/suites/actions/toolbar-single-selection.test.ts +++ b/e2e/suites/actions/toolbar-single-selection.test.ts @@ -25,22 +25,21 @@ import { browser, protractor } from 'protractor'; import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs'; import { RepoClient } from '../../utilities/repo-client/repo-client'; import { Utils } from '../../utilities/utils'; describe('Toolbar actions - single selection : ', () => { const username = `user-${Utils.random()}`; - const username2 = `user-${Utils.random()}`; const fileUser = `fileUser-${Utils.random()}.txt`; let fileUserId; const folderUser = `folderUser-${Utils.random()}`; let folderUserId; const fileForDelete = `fileForDelete-${Utils.random()}.txt`; let fileForDeleteId; const folderForDelete = `folderForDelete-${Utils.random()}`; let folderForDeleteId; - const siteName = `site-private-${Utils.random()}`; - const fileAdmin = `fileAdmin-${Utils.random()}.txt`; - const folderAdmin = `folderAdmin-${Utils.random()}`; + const siteName = `site-${Utils.random()}`; + const fileInSite = `fileAdmin-${Utils.random()}.txt`; + const folderInSite = `folderAdmin-${Utils.random()}`; const apis = { admin: new RepoClient(), @@ -55,10 +54,10 @@ describe('Toolbar actions - single selection : ', () => { beforeAll(async (done) => { await apis.admin.people.createUser({ username }); - fileUserId = (await apis.user.nodes.createFiles([fileUser])).entry.id; - fileForDeleteId = (await apis.user.nodes.createFiles([fileForDelete])).entry.id; - folderForDeleteId = (await apis.user.nodes.createFolders([ folderForDelete ])).entry.id; - folderUserId = (await apis.user.nodes.createFolders([ folderUser ])).entry.id; + fileUserId = (await apis.user.nodes.createFile(fileUser)).entry.id; + fileForDeleteId = (await apis.user.nodes.createFile(fileForDelete)).entry.id; + folderForDeleteId = (await apis.user.nodes.createFolder(folderForDelete)).entry.id; + folderUserId = (await apis.user.nodes.createFolder(folderUser)).entry.id; await apis.user.shared.shareFileById(fileUserId); await apis.user.shared.waitForApi({ expect: 1 }); @@ -67,42 +66,37 @@ describe('Toolbar actions - single selection : ', () => { await apis.user.favorites.addFavoriteById('folder', folderUserId); await apis.user.favorites.waitForApi({ expect: 2 }); + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE); + const docLibId = await apis.user.sites.getDocLibId(siteName); + + await apis.user.nodes.createFile(fileInSite, docLibId); + await apis.user.nodes.createFolder(folderInSite, docLibId); + + await apis.user.nodes.deleteNodeById(fileForDeleteId, false); + await apis.user.nodes.deleteNodeById(folderForDeleteId, false); + + await loginPage.loginWith(username); done(); }); afterAll(async (done) => { await Promise.all([ apis.user.nodes.deleteNodeById(fileUserId), - apis.user.nodes.deleteNodeById(folderUserId) + apis.user.nodes.deleteNodeById(folderUserId), + apis.user.sites.deleteSite(siteName), + apis.user.trashcan.emptyTrash(), + logoutPage.load() ]); - done(); }); xit(''); describe('General tests', () => { - const userSite = `userSite-${Utils.random()}`; - - beforeAll(async (done) => { - await apis.user.sites.createSite(userSite, SITE_VISIBILITY.PUBLIC); - await loginPage.loginWith(username); - done(); - }); - - afterAll(async (done) => { - await Promise.all([ - apis.user.sites.deleteSite(userSite), - logoutPage.load() - ]); - - done(); - }); - it('actions not displayed for top level of File Libraries - [C213135]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); await dataTable.waitForHeader(); - await dataTable.selectItem(userSite); + await dataTable.selectItem(siteName); expect(await toolbar.actions.isEmpty()).toBe(true, 'toolbar not empty'); }); @@ -114,210 +108,15 @@ describe('Toolbar actions - single selection : ', () => { }); }); - describe('granular permissions', () => { - const site = `site-${Utils.random()}`; - const file1 = `file1-${Utils.random()}`; let file1Id; - const file2 = `file2-${Utils.random()}`; let file2Id; - - beforeAll(async (done) => { - await apis.admin.sites.createSite(site, SITE_VISIBILITY.PRIVATE); - const docLibId = await apis.admin.sites.getDocLibId(site); - - file1Id = (await apis.admin.nodes.createFile(file1, docLibId)).entry.id; - file2Id = (await apis.admin.nodes.createFile(file2, docLibId)).entry.id; - - await apis.admin.sites.addSiteMember(site, username, SITE_ROLES.SITE_CONSUMER); - await apis.admin.nodes.setGranularPermission(file1Id, false, username, SITE_ROLES.SITE_CONSUMER); - await apis.admin.nodes.setGranularPermission(file2Id, false, username, SITE_ROLES.SITE_MANAGER); - - await apis.user.shared.shareFileById(file1Id); - await apis.admin.shared.shareFileById(file2Id); - await apis.user.shared.waitForApi({ expect: 3 }); - - await apis.user.favorites.addFavoritesByIds('file', [file1Id, file2Id]); - await apis.user.favorites.waitForApi({ expect: 3 }); - - await loginPage.loginWith(username); - done(); - }); - - afterAll(async (done) => { - await Promise.all([ - apis.admin.sites.deleteSite(site), - logoutPage.load() - ]); - - done(); - }); - - describe('actions update accordingly for files with different granular permissions', () => { - beforeEach(async (done) => { - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - done(); - }); - - it('on File Libraries - [C280455]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - await dataTable.doubleClickOnRowByName(site); - await dataTable.waitForHeader(); - await dataTable.selectItem(file1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); - - let menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); - await toolbar.actions.closeMoreMenu(); - - await dataTable.selectItem(file2); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file2}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file2}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file2}`); - - menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file2}`); - await toolbar.actions.closeMoreMenu(); - }); - - it('on Shared Files - [C280456]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await page.dataTable.waitForHeader(); - await page.dataTable.selectItem(file1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); - let menu = await toolbar.actions.openMoreMenu(); - - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); - await toolbar.actions.closeMoreMenu(); - - await page.dataTable.selectItem(file2); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file2}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file2}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file2}`); - menu = await toolbar.actions.openMoreMenu(); - - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file2}`); - await toolbar.actions.closeMoreMenu(); - }); - - // disabled until ACA-1737 is done - xit('on Favorites - [C213121]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); - await dataTable.selectItem(file1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); - let menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); - await toolbar.actions.closeMoreMenu(); - - await dataTable.selectItem(file2); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file2}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file2}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file2}`); - menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${file2}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file2}`); - await toolbar.actions.closeMoreMenu(); - }); - }); - - describe('correct actions are displayed when selecting multiple files with different granular permissions', () => { - beforeEach(async (done) => { - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - done(); - }); - - it('on File Libraries - [C280476]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - await dataTable.doubleClickOnRowByName(site); - await dataTable.waitForHeader(); - await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); - }); - - it('on Shared Files - [C280477]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); - await dataTable.waitForHeader(); - await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); - }); - - // disabled until ACA-1737 is done - xit('on Favorites - [C280478]', async () => { - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); - await dataTable.waitForHeader(); - await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); - }); - }); - - xit(''); - }); - describe('Personal Files', () => { - beforeAll(async (done) => { - await loginPage.loginWith(username); - done(); - }); - beforeEach(async (done) => { await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await dataTable.clearSelection(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('actions are not displayed when no item is selected - [C213120]', async () => { expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); @@ -352,139 +151,49 @@ describe('Toolbar actions - single selection : ', () => { }); describe('File Libraries', () => { - beforeAll(async (done) => { - await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); - const docLibId = await apis.admin.sites.getDocLibId(siteName); - - await apis.admin.people.createUser({ username: username2 }); - await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER); - await apis.admin.sites.addSiteMember(siteName, username2, SITE_ROLES.SITE_CONSUMER); - - await apis.admin.nodes.createFile(fileAdmin, docLibId); - await apis.admin.nodes.createFolder(folderAdmin, docLibId); + beforeEach(async (done) => { + await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); done(); }); - afterAll(async (done) => { - await apis.admin.sites.deleteSite(siteName); - done(); + it('actions are not displayed when no item is selected - [C280439]', async () => { + expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); - xit(''); - - describe('user is Manager', () => { - beforeAll(async (done) => { - await loginPage.loginWith(username); - done(); - }); - - beforeEach(async (done) => { - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - await dataTable.doubleClickOnRowByName(siteName); - await dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - - it('actions are not displayed when no item is selected - [C280439]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); - }); - - it('correct actions appear when a file is selected - [C280440]', async () => { - await dataTable.selectItem(fileAdmin); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileAdmin}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileAdmin}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileAdmin}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileAdmin}`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileAdmin}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileAdmin}`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileAdmin}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileAdmin}`); - await toolbar.actions.closeMoreMenu(); - }); - - it('correct actions appear when a folder is selected - [C280441]', async () => { - await dataTable.selectItem(folderAdmin); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderAdmin}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderAdmin}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderAdmin}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderAdmin}`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderAdmin}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderAdmin}`); - expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderAdmin}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderAdmin}`); - await toolbar.actions.closeMoreMenu(); - }); + it('correct actions appear when a file is selected - [C280440]', async () => { + await dataTable.selectItem(fileInSite); + expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileInSite}`); + expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileInSite}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileInSite}`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileInSite}`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileInSite}`); + expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileInSite}`); + expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileInSite}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileInSite}`); + await toolbar.actions.closeMoreMenu(); }); - describe('user is Consumer', () => { - beforeAll(async (done) => { - await loginPage.loginWith(username2); - done(); - }); - - beforeEach(async (done) => { - await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); - await dataTable.waitForHeader(); - await dataTable.doubleClickOnRowByName(siteName); - await dataTable.waitForHeader(); - done(); - }); - - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - - it('actions are not displayed when no item is selected - [C280442]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); - }); - - it('correct actions appear when a file is selected - [C280443]', async () => { - await dataTable.selectItem(fileAdmin); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileAdmin}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileAdmin}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileAdmin}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileAdmin}`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileAdmin}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${fileAdmin}`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${fileAdmin}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileAdmin}`); - await toolbar.actions.closeMoreMenu(); - }); - - it('correct actions appear when a folder is selected - [C280444]', async () => { - await dataTable.selectItem(folderAdmin); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderAdmin}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderAdmin}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderAdmin}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folderAdmin}`); - const menu = await toolbar.actions.openMoreMenu(); - expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderAdmin}`); - expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folderAdmin}`); - expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folderAdmin}`); - expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderAdmin}`); - await toolbar.actions.closeMoreMenu(); - }); + it('correct actions appear when a folder is selected - [C280441]', async () => { + await dataTable.selectItem(folderInSite); + expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderInSite}`); + expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderInSite}`); + expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderInSite}`); + expect(await toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderInSite}`); + const menu = await toolbar.actions.openMoreMenu(); + expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderInSite}`); + expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderInSite}`); + expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderInSite}`); + expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderInSite}`); + await toolbar.actions.closeMoreMenu(); }); }); describe('Shared Files', () => { - beforeAll(async (done) => { - await loginPage.loginWith(username); - done(); - }); - beforeEach(async (done) => { await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); @@ -492,16 +201,11 @@ describe('Toolbar actions - single selection : ', () => { done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('actions are not displayed when no item is selected - [C280445]', async () => { expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); - it('correct actions appear when a file is selected - [C280446]', async () => { + it('correct actions appear when a file is selected - [C286265]', async () => { await page.dataTable.selectItem(fileUser); expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); @@ -514,14 +218,10 @@ describe('Toolbar actions - single selection : ', () => { expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); await toolbar.actions.closeMoreMenu(); }); + }); describe('Recent Files', () => { - beforeAll(async (done) => { - await loginPage.loginWith(username); - done(); - }); - beforeEach(async (done) => { await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); @@ -529,11 +229,6 @@ describe('Toolbar actions - single selection : ', () => { done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('actions are not displayed when no item is selected - [C280447]', async () => { expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); @@ -554,11 +249,6 @@ describe('Toolbar actions - single selection : ', () => { }); describe('Favorites', () => { - beforeAll(async (done) => { - await loginPage.loginWith(username); - done(); - }); - beforeEach(async (done) => { await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); @@ -566,11 +256,6 @@ describe('Toolbar actions - single selection : ', () => { done(); }); - afterAll(async (done) => { - await logoutPage.load(); - done(); - }); - it('actions are not displayed when no item is selected - [C280449]', async () => { expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); @@ -605,13 +290,6 @@ describe('Toolbar actions - single selection : ', () => { }); describe('Trash', () => { - beforeAll(async (done) => { - await apis.user.nodes.deleteNodeById(fileForDeleteId, false); - await apis.user.nodes.deleteNodeById(folderForDeleteId, false); - await loginPage.loginWith(username); - done(); - }); - beforeEach(async (done) => { await browser.actions().sendKeys(protractor.Key.ESCAPE); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); @@ -619,15 +297,6 @@ describe('Toolbar actions - single selection : ', () => { done(); }); - afterAll(async (done) => { - await Promise.all([ - apis.user.trashcan.permanentlyDelete(fileForDeleteId), - apis.user.trashcan.permanentlyDelete(folderForDeleteId), - logoutPage.load() - ]); - done(); - }); - it('actions are not displayed when no item is selected - [C280452]', async () => { expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); From aafa606ceba365f8d62461e2ee1c37796a130a4e Mon Sep 17 00:00:00 2001 From: Adina Parpalita Date: Thu, 13 Sep 2018 11:01:16 +0300 Subject: [PATCH 009/160] split toolbar and breadcrumb e2e components (#626) --- .../breadcrumb.ts} | 10 +- e2e/components/components.ts | 1 + e2e/components/toolbar/toolbar-actions.ts | 73 -------- e2e/components/toolbar/toolbar.ts | 43 ++++- e2e/components/viewer/viewer.ts | 4 +- e2e/pages/browsing-page.ts | 3 +- e2e/suites/actions/delete-undo-delete.test.ts | 128 +++++++------- e2e/suites/actions/edit-folder.test.ts | 2 +- e2e/suites/actions/mark-favorite.test.ts | 118 ++++++------- e2e/suites/actions/permanently-delete.test.ts | 10 +- e2e/suites/actions/restore.test.ts | 16 +- e2e/suites/actions/single-click.test.ts | 3 +- ...cial-permissions-available-actions.test.ts | 160 +++++++++--------- .../toolbar-multiple-selection.test.ts | 106 ++++++------ .../actions/toolbar-single-selection.test.ts | 122 ++++++------- e2e/suites/extensions/ext-info-drawer.test.ts | 12 +- e2e/suites/list-views/favorites.test.ts | 3 +- e2e/suites/list-views/recent-files.test.ts | 3 +- e2e/suites/list-views/shared-files.test.ts | 3 +- e2e/suites/list-views/trash.test.ts | 3 +- e2e/suites/navigation/breadcrumb.test.ts | 2 +- e2e/suites/viewer/viewer-general.test.ts | 17 +- 22 files changed, 398 insertions(+), 444 deletions(-) rename e2e/components/{toolbar/toolbar-breadcrumb.ts => breadcrumb/breadcrumb.ts} (84%) delete mode 100755 e2e/components/toolbar/toolbar-actions.ts diff --git a/e2e/components/toolbar/toolbar-breadcrumb.ts b/e2e/components/breadcrumb/breadcrumb.ts similarity index 84% rename from e2e/components/toolbar/toolbar-breadcrumb.ts rename to e2e/components/breadcrumb/breadcrumb.ts index 7b939d120..131bd5483 100755 --- a/e2e/components/toolbar/toolbar-breadcrumb.ts +++ b/e2e/components/breadcrumb/breadcrumb.ts @@ -26,18 +26,18 @@ import { ElementFinder, ElementArrayFinder, by, promise } from 'protractor'; import { Component } from '../component'; -export class ToolbarBreadcrumb extends Component { +export class Breadcrumb extends Component { private static selectors = { root: 'adf-breadcrumb', item: '.adf-breadcrumb-item', currentItem: '.adf-breadcrumb-item-current' }; - items: ElementArrayFinder = this.component.all(by.css(ToolbarBreadcrumb.selectors.item)); - currentItem: ElementFinder = this.component.element(by.css(ToolbarBreadcrumb.selectors.currentItem)); + items: ElementArrayFinder = this.component.all(by.css(Breadcrumb.selectors.item)); + currentItem: ElementFinder = this.component.element(by.css(Breadcrumb.selectors.currentItem)); constructor(ancestor?: ElementFinder) { - super(ToolbarBreadcrumb.selectors.root, ancestor); + super(Breadcrumb.selectors.root, ancestor); } getNthItem(nth: number): ElementFinder { @@ -69,7 +69,7 @@ export class ToolbarBreadcrumb extends Component { } clickItem(name: string) { - return this.component.element(by.css(`${ToolbarBreadcrumb.selectors.item}[title=${name}]`)).click(); + return this.component.element(by.css(`${Breadcrumb.selectors.item}[title=${name}]`)).click(); } clickNthItem(nth: number) { diff --git a/e2e/components/components.ts b/e2e/components/components.ts index fb8911cd2..f8d1b44b5 100755 --- a/e2e/components/components.ts +++ b/e2e/components/components.ts @@ -32,4 +32,5 @@ export * from './dialog/create-edit-folder-dialog'; export * from './pagination/pagination'; export * from './sidenav/sidenav'; export * from './toolbar/toolbar'; +export * from './breadcrumb/breadcrumb'; export * from './viewer/viewer'; diff --git a/e2e/components/toolbar/toolbar-actions.ts b/e2e/components/toolbar/toolbar-actions.ts deleted file mode 100755 index 00c07003c..000000000 --- a/e2e/components/toolbar/toolbar-actions.ts +++ /dev/null @@ -1,73 +0,0 @@ -/*! - * @license - * Alfresco Example Content Application - * - * Copyright (C) 2005 - 2018 Alfresco Software Limited - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -import { ElementFinder, ElementArrayFinder, by, promise, protractor, browser } from 'protractor'; -import { Menu } from '../menu/menu'; -import { Component } from '../component'; - -export class ToolbarActions extends Component { - private static selectors = { - root: 'adf-toolbar', - button: '.mat-icon-button' - }; - - menu: Menu = new Menu(); - buttons: ElementArrayFinder = this.component.all(by.css(ToolbarActions.selectors.button)); - - constructor(ancestor?: ElementFinder) { - super(ToolbarActions.selectors.root, ancestor); - } - - async isEmpty() { - return await this.buttons.count() === 0; - } - - async isButtonPresent(title: string) { - return await this.component.element(by.css(`${ToolbarActions.selectors.button}[title="${title}"]`)).isPresent(); - } - - getButtonByLabel(label: string) { - return this.component.element(by.cssContainingText(ToolbarActions.selectors.button, label)); - } - - getButtonByTitleAttribute(title: string) { - return this.component.element(by.css(`${ToolbarActions.selectors.button}[title="${title}"]`)); - } - - async openMoreMenu() { - await this.getButtonByTitleAttribute('More actions').click(); - await this.menu.waitForMenuToOpen(); - return this.menu; - } - - async closeMoreMenu() { - return await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - } - - async getButtonTooltip(button: ElementFinder) { - return await button.getAttribute('title'); - } - -} diff --git a/e2e/components/toolbar/toolbar.ts b/e2e/components/toolbar/toolbar.ts index 7bfbcfba0..cd07acb1d 100755 --- a/e2e/components/toolbar/toolbar.ts +++ b/e2e/components/toolbar/toolbar.ts @@ -23,20 +23,51 @@ * along with Alfresco. If not, see . */ -import { ElementFinder } from 'protractor'; +import { ElementFinder, ElementArrayFinder, by, promise, protractor, browser } from 'protractor'; +import { Menu } from '../menu/menu'; import { Component } from '../component'; -import { ToolbarActions } from './toolbar-actions'; -import { ToolbarBreadcrumb } from './toolbar-breadcrumb'; export class Toolbar extends Component { private static selectors = { - root: '.inner-layout__header' + root: 'adf-toolbar', + button: '.mat-icon-button' }; - actions: ToolbarActions = new ToolbarActions(this.component); - breadcrumb: ToolbarBreadcrumb = new ToolbarBreadcrumb(this.component); + menu: Menu = new Menu(); + buttons: ElementArrayFinder = this.component.all(by.css(Toolbar.selectors.button)); constructor(ancestor?: ElementFinder) { super(Toolbar.selectors.root, ancestor); } + + async isEmpty() { + return await this.buttons.count() === 0; + } + + async isButtonPresent(title: string) { + return await this.component.element(by.css(`${Toolbar.selectors.button}[title="${title}"]`)).isPresent(); + } + + getButtonByLabel(label: string) { + return this.component.element(by.cssContainingText(Toolbar.selectors.button, label)); + } + + getButtonByTitleAttribute(title: string) { + return this.component.element(by.css(`${Toolbar.selectors.button}[title="${title}"]`)); + } + + async openMoreMenu() { + await this.getButtonByTitleAttribute('More actions').click(); + await this.menu.waitForMenuToOpen(); + return this.menu; + } + + async closeMoreMenu() { + return await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); + } + + async getButtonTooltip(button: ElementFinder) { + return await button.getAttribute('title'); + } + } diff --git a/e2e/components/viewer/viewer.ts b/e2e/components/viewer/viewer.ts index 9b643cbca..919f3528b 100755 --- a/e2e/components/viewer/viewer.ts +++ b/e2e/components/viewer/viewer.ts @@ -26,7 +26,7 @@ import { ElementFinder, by, browser, ExpectedConditions as EC } from 'protractor'; import { Component } from '../component'; import { BROWSER_WAIT_TIMEOUT } from '../../configs'; -import { ToolbarActions } from '../toolbar/toolbar-actions'; +import { Toolbar } from '../toolbar/toolbar'; export class Viewer extends Component { private static selectors = { @@ -46,7 +46,7 @@ export class Viewer extends Component { fileTitle: ElementFinder = this.component.element(by.css(Viewer.selectors.fileTitle)); viewerExtensionContent: ElementFinder = this.component.element(by.css(Viewer.selectors.viewerExtensionContent)); - toolbar = new ToolbarActions(this.component); + toolbar = new Toolbar(this.component); constructor(ancestor?: ElementFinder) { super(Viewer.selectors.root, ancestor); diff --git a/e2e/pages/browsing-page.ts b/e2e/pages/browsing-page.ts index d5eab3de1..8674bc901 100755 --- a/e2e/pages/browsing-page.ts +++ b/e2e/pages/browsing-page.ts @@ -24,13 +24,14 @@ */ import { promise } from 'protractor'; -import { Header, DataTable, Pagination, Toolbar, Sidenav } from '../components/components'; +import { Header, DataTable, Pagination, Toolbar, Breadcrumb, Sidenav } from '../components/components'; import { Page } from './page'; export class BrowsingPage extends Page { header = new Header(this.app); sidenav = new Sidenav(this.app); toolbar = new Toolbar(this.app); + breadcrumb = new Breadcrumb(this.app); dataTable = new DataTable(this.app); pagination = new Pagination(this.app); diff --git a/e2e/suites/actions/delete-undo-delete.test.ts b/e2e/suites/actions/delete-undo-delete.test.ts index f73b64ba6..830ee4251 100755 --- a/e2e/suites/actions/delete-undo-delete.test.ts +++ b/e2e/suites/actions/delete-undo-delete.test.ts @@ -107,8 +107,8 @@ describe('Delete and undo delete', () => { let items = await page.dataTable.countRows(); await dataTable.selectItem(file1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`${file1} deleted`); expect(dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not removed from list'); @@ -124,8 +124,8 @@ describe('Delete and undo delete', () => { let items = await page.dataTable.countRows(); await dataTable.selectMultipleItems([file1, file2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Deleted 2 items`); expect(dataTable.getRowByName(file1).isPresent()).toBe(false, `${file1} was not removed from list`); @@ -144,8 +144,8 @@ describe('Delete and undo delete', () => { let items = await page.dataTable.countRows(); await dataTable.selectItem(folder1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); expect(dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not removed from list'); items--; expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); @@ -158,8 +158,8 @@ describe('Delete and undo delete', () => { it('delete a folder containing locked files - [C217127]', async () => { await dataTable.selectItem(folder2); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`${folder2} couldn't be deleted`); expect(dataTable.getRowByName(folder2).isPresent()).toBe(true, 'Item was removed from list'); @@ -170,8 +170,8 @@ describe('Delete and undo delete', () => { it('notification on multiple items deletion - some items fail to delete - [C217129]', async () => { await dataTable.selectMultipleItems([file1, folder2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Deleted 1 item, 1 couldn't be deleted`); @@ -180,16 +180,16 @@ describe('Delete and undo delete', () => { it('notification on multiple items deletion - all items fail to delete - [C217130]', async () => { await dataTable.selectMultipleItems([fileLocked1, folder2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toEqual(`2 items couldn't be deleted`); }); it('successful delete notification shows Undo action - [C217131]', async () => { await dataTable.selectItem(file1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Undo`); @@ -198,8 +198,8 @@ describe('Delete and undo delete', () => { it('unsuccessful delete notification does not show Undo action - [C217134]', async () => { await dataTable.selectItem(folder2); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).not.toContain(`Undo`); }); @@ -208,8 +208,8 @@ describe('Delete and undo delete', () => { const items = await page.dataTable.countRows(); await dataTable.selectItem(file1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); expect(dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item was not restored'); expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); @@ -219,8 +219,8 @@ describe('Delete and undo delete', () => { const items = await page.dataTable.countRows(); await dataTable.selectItem(folder1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); expect(dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item was not restored'); expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); @@ -232,8 +232,8 @@ describe('Delete and undo delete', () => { const items = await page.dataTable.countRows(); await dataTable.selectMultipleItems([file1, file2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); expect(dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} was not removed from list`); expect(dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} was not removed from list`); @@ -281,8 +281,8 @@ describe('Delete and undo delete', () => { it('delete a file and check notification - [C280316]', async () => { await dataTable.selectItem(sharedFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`${sharedFile1} deleted`); expect(dataTable.getRowByName(sharedFile1).isPresent()).toBe(false, 'Item was not removed from list'); @@ -296,8 +296,8 @@ describe('Delete and undo delete', () => { it('delete multiple files and check notification - [C280513]', async () => { await dataTable.selectMultipleItems([sharedFile2, sharedFile3]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Deleted 2 items`); expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, `${sharedFile2} was not removed from list`); @@ -314,8 +314,8 @@ describe('Delete and undo delete', () => { it('successful delete notification shows Undo action - [C280323]', async () => { await dataTable.selectItem(sharedFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Undo`); @@ -324,8 +324,8 @@ describe('Delete and undo delete', () => { xit('undo delete of file - [C280324]', async () => { await dataTable.selectItem(sharedFile2); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); expect(dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, 'Item was not restored'); @@ -333,8 +333,8 @@ describe('Delete and undo delete', () => { xit('undo delete of multiple files - [C280514]', async () => { await dataTable.selectMultipleItems([sharedFile3, sharedFile4]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); expect(dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not restored`); @@ -401,8 +401,8 @@ describe('Delete and undo delete', () => { let items = await page.dataTable.countRows(); await dataTable.selectItem(favoriteFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`${favoriteFile1} deleted`); expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, 'Item was not removed from list'); @@ -418,8 +418,8 @@ describe('Delete and undo delete', () => { let items = await page.dataTable.countRows(); await dataTable.selectMultipleItems([favoriteFile1, favoriteFile2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Deleted 2 items`); expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, `${favoriteFile1} was not removed from list`); @@ -437,8 +437,8 @@ describe('Delete and undo delete', () => { it('delete a folder with content - [C280518]', async () => { let items = await page.dataTable.countRows(); await dataTable.selectItem(favoriteFolder1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(false, 'Item was not removed from list'); items--; expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); @@ -451,8 +451,8 @@ describe('Delete and undo delete', () => { it('delete a folder containing locked files - [C280519]', async () => { await dataTable.selectItem(favoriteFolder2); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`${favoriteFolder2} couldn't be deleted`); expect(dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(true, 'Item was removed from list'); @@ -463,8 +463,8 @@ describe('Delete and undo delete', () => { it('notification on multiple items deletion - some items fail to delete - [C280520]', async () => { await dataTable.selectMultipleItems([favoriteFile1, favoriteFolder2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Deleted 1 item, 1 couldn't be deleted`); @@ -473,16 +473,16 @@ describe('Delete and undo delete', () => { it('notification on multiple items deletion - all items fail to delete - [C280521]', async () => { await dataTable.selectMultipleItems([favoriteFileLocked1, favoriteFolder2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toEqual(`2 items couldn't be deleted`); }); it('successful delete notification shows Undo action - [C280522]', async () => { await dataTable.selectItem(favoriteFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Undo`); @@ -491,8 +491,8 @@ describe('Delete and undo delete', () => { it('unsuccessful delete notification does not show Undo action - [C280523]', async () => { await dataTable.selectItem(favoriteFolder2); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).not.toContain(`Undo`); }); @@ -501,8 +501,8 @@ describe('Delete and undo delete', () => { const items = await page.dataTable.countRows(); await dataTable.selectItem(favoriteFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item was not restored'); expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); @@ -512,8 +512,8 @@ describe('Delete and undo delete', () => { const items = await page.dataTable.countRows(); await dataTable.selectItem(favoriteFolder1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); expect(dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item was not restored'); expect(page.pagination.range.getText()).toContain(`1-${items} of ${items}`); @@ -525,8 +525,8 @@ describe('Delete and undo delete', () => { const items = await page.dataTable.countRows(); await dataTable.selectMultipleItems([favoriteFile1, favoriteFile2]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); expect(dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} was not removed from list`); expect(dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} was not removed from list`); @@ -580,8 +580,8 @@ describe('Delete and undo delete', () => { xit('delete a file and check notification - [C280528]', async () => { await dataTable.selectItem(recentFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`${recentFile1} deleted`); expect(dataTable.getRowByName(recentFile1).isPresent()).toBe(false, 'Item was not removed from list'); @@ -594,8 +594,8 @@ describe('Delete and undo delete', () => { xit('delete multiple files and check notification - [C280529]', async () => { await dataTable.selectMultipleItems([recentFile2, recentFile3]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Deleted 2 items`); expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(false, `${recentFile2} was not removed from list`); @@ -611,8 +611,8 @@ describe('Delete and undo delete', () => { xit('successful delete notification shows Undo action - [C280534]', async () => { await dataTable.selectItem(recentFile1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); const message = await page.getSnackBarMessage(); expect(message).toContain(`Undo`); @@ -626,8 +626,8 @@ describe('Delete and undo delete', () => { // so for the moment we're testing that the restored file is not displayed in the Trash xit('undo delete of file - [C280536]', async () => { await dataTable.selectItem(recentFile2); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); expect(dataTable.getRowByName(recentFile2).isPresent()).toBe(false, 'Item is in Trash'); @@ -639,8 +639,8 @@ describe('Delete and undo delete', () => { // so for the moment we're testing that the restored file is not displayed in the Trash xit('undo delete of multiple files - [C280537]', async () => { await dataTable.selectMultipleItems([recentFile3, recentFile4]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Delete'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Delete'); await page.clickSnackBarAction(); await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH); expect(dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} is in Trash`); diff --git a/e2e/suites/actions/edit-folder.test.ts b/e2e/suites/actions/edit-folder.test.ts index 32e4ce3f4..e5962698f 100755 --- a/e2e/suites/actions/edit-folder.test.ts +++ b/e2e/suites/actions/edit-folder.test.ts @@ -55,7 +55,7 @@ describe('Edit folder', () => { const personalFilesPage = new BrowsingPage(); const editDialog = new CreateOrEditFolderDialog(); const { dataTable } = personalFilesPage; - const editButton = personalFilesPage.toolbar.actions.getButtonByTitleAttribute('Edit'); + const editButton = personalFilesPage.toolbar.getButtonByTitleAttribute('Edit'); beforeAll(done => { apis.admin.people.createUser({ username }) diff --git a/e2e/suites/actions/mark-favorite.test.ts b/e2e/suites/actions/mark-favorite.test.ts index f5a7cba64..85b9ec140 100644 --- a/e2e/suites/actions/mark-favorite.test.ts +++ b/e2e/suites/actions/mark-favorite.test.ts @@ -93,31 +93,31 @@ describe('Mark items as favorites', () => { }); beforeEach(done => { - toolbar.actions.closeMoreMenu().then(done); + toolbar.closeMoreMenu().then(done); }); it('Favorite action has empty star icon for an item not marked as favorite - [C217186]', () => { dataTable.selectItem(file1NotFav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => expect(toolbar.actions.menu.getItemIconText('Favorite')).toEqual('star_border')); + .then(() => toolbar.openMoreMenu()) + .then(() => expect(toolbar.menu.getItemIconText('Favorite')).toEqual('star_border')); }); it('Favorite action has empty star icon for multiple selection of items when some are not favorite - [C217187]', () => { dataTable.selectMultipleItems([ file1NotFav, file3Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => expect(toolbar.actions.menu.getItemIconText('Favorite')).toEqual('star_border')); + .then(() => toolbar.openMoreMenu()) + .then(() => expect(toolbar.menu.getItemIconText('Favorite')).toEqual('star_border')); }); it('Favorite action has full star icon for items marked as favorite - [C217188]', () => { dataTable.selectItem(file3Fav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => expect(toolbar.actions.menu.getItemIconText('Favorite')).toEqual('star')); + .then(() => toolbar.openMoreMenu()) + .then(() => expect(toolbar.menu.getItemIconText('Favorite')).toEqual('star')); }); it('favorite a file - [C217189]', () => { dataTable.selectItem(file1NotFav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => apis.user.favorites.isFavorite(file1Id)) .then(isFavorite => expect(isFavorite).toBe(true, `${file1NotFav} not marked as favorite`)) @@ -127,8 +127,8 @@ describe('Mark items as favorites', () => { it('favorite a folder - [C280390]', () => { dataTable.selectItem(folder1) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => apis.user.favorites.isFavorite(folder1Id)) .then(isFavorite => expect(isFavorite).toBe(true, `${folder1} not marked as favorite`)) @@ -138,8 +138,8 @@ describe('Mark items as favorites', () => { it('unfavorite an item - [C217190]', () => { dataTable.selectItem(file3Fav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 1 })) .then(() => apis.user.favorites.isFavorite(file3Id)) .then(isFavorite => expect(isFavorite).toBe(false, `${file3Fav} is marked as favorite`)) @@ -149,8 +149,8 @@ describe('Mark items as favorites', () => { it('favorite multiple items - all unfavorite - [C217192]', () => { dataTable.selectMultipleItems([ file1NotFav, file2NotFav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 4 })) .then(() => Promise.all([ apis.user.favorites.isFavorite(file1Id), @@ -167,8 +167,8 @@ describe('Mark items as favorites', () => { it('favorite multiple items - some favorite and some unfavorite - [C217194]', () => { dataTable.selectMultipleItems([ file1NotFav, file3Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => Promise.all([ apis.user.favorites.isFavorite(file1Id), @@ -184,8 +184,8 @@ describe('Mark items as favorites', () => { it('unfavorite multiple items - [C217193]', () => { dataTable.selectMultipleItems([ file3Fav, file4Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => browser.sleep(2000)) .then(() => Promise.all([ apis.user.favorites.isFavorite(file3Id), @@ -209,13 +209,13 @@ describe('Mark items as favorites', () => { }); beforeEach(done => { - toolbar.actions.closeMoreMenu().then(done); + toolbar.closeMoreMenu().then(done); }); it('favorite a file - [C280352]', () => { dataTable.selectItem(file1NotFav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => apis.user.favorites.isFavorite(file1Id)) .then(isFavorite => expect(isFavorite).toBe(true, `${file1NotFav} not marked as favorite`)) @@ -225,8 +225,8 @@ describe('Mark items as favorites', () => { it('unfavorite an item - [C280353]', () => { dataTable.selectItem(file3Fav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 1 })) .then(() => apis.user.favorites.isFavorite(file3Id)) .then(isFavorite => expect(isFavorite).toBe(false, `${file3Fav} is marked as favorite`)) @@ -236,8 +236,8 @@ describe('Mark items as favorites', () => { it('favorite multiple items - all unfavorite - [C280355]', () => { dataTable.selectMultipleItems([ file1NotFav, file2NotFav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 4 })) .then(() => Promise.all([ apis.user.favorites.isFavorite(file1Id), @@ -254,8 +254,8 @@ describe('Mark items as favorites', () => { it('favorite multiple items - some favorite and some unfavorite - [C280357]', () => { dataTable.selectMultipleItems([ file1NotFav, file3Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => Promise.all([ apis.user.favorites.isFavorite(file1Id), @@ -271,8 +271,8 @@ describe('Mark items as favorites', () => { it('unfavorite multiple items - [C280356]', () => { dataTable.selectMultipleItems([ file3Fav, file4Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => browser.sleep(2000)) .then(() => Promise.all([ apis.user.favorites.isFavorite(file3Id), @@ -304,8 +304,8 @@ describe('Mark items as favorites', () => { it('favorite a file - [C280362]', () => { dataTable.selectItem(file1NotFav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => apis.user.favorites.isFavorite(file1Id)) .then(isFavorite => expect(isFavorite).toBe(true, `${file1NotFav} not marked as favorite`)) @@ -315,8 +315,8 @@ describe('Mark items as favorites', () => { it('unfavorite an item - [C280363]', () => { dataTable.selectItem(file3Fav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 1 })) .then(() => apis.user.favorites.isFavorite(file3Id)) .then(isFavorite => expect(isFavorite).toBe(false, `${file3Fav} is marked as favorite`)) @@ -326,8 +326,8 @@ describe('Mark items as favorites', () => { it('favorite multiple items - all unfavorite - [C280365]', () => { dataTable.selectMultipleItems([ file1NotFav, file2NotFav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 4 })) .then(() => Promise.all([ apis.user.favorites.isFavorite(file1Id), @@ -344,8 +344,8 @@ describe('Mark items as favorites', () => { it('favorite multiple items - some favorite and some unfavorite - [C280367]', () => { dataTable.selectMultipleItems([ file1NotFav, file3Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 3 })) .then(() => Promise.all([ apis.user.favorites.isFavorite(file1Id), @@ -361,8 +361,8 @@ describe('Mark items as favorites', () => { it('unfavorite multiple items - [C280366]', () => { dataTable.selectMultipleItems([ file3Fav, file4Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => browser.sleep(2000)) .then(() => Promise.all([ apis.user.favorites.isFavorite(file3Id), @@ -391,8 +391,8 @@ describe('Mark items as favorites', () => { it('unfavorite an item - [C280368]', () => { dataTable.selectItem(file3Fav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => apis.user.favorites.waitForApi({ expect: 1 })) .then(() => apis.user.favorites.isFavorite(file3Id)) .then(isFavorite => { @@ -405,8 +405,8 @@ describe('Mark items as favorites', () => { it('unfavorite multiple items - [C280374]', () => { dataTable.selectMultipleItems([ file3Fav, file4Fav ]) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => toolbar.actions.menu.clickMenuItem('Favorite')) + .then(() => toolbar.openMoreMenu()) + .then(() => toolbar.menu.clickMenuItem('Favorite')) .then(() => browser.sleep(2000)) .then(() => apis.user.favorites.isFavorite(file3Id)) .then(resp => { @@ -425,8 +425,8 @@ describe('Mark items as favorites', () => { it('Favorite action has full star icon for items marked as favorite - [C280371]', () => { dataTable.selectItem(file3Fav) - .then(() => toolbar.actions.openMoreMenu()) - .then(() => expect(toolbar.actions.menu.getItemIconText('Favorite')).toEqual('star')); + .then(() => toolbar.openMoreMenu()) + .then(() => expect(toolbar.menu.getItemIconText('Favorite')).toEqual('star')); }); }); @@ -451,7 +451,7 @@ describe('Mark items as favorites', () => { await fileLibrariesPage.dataTable.waitForHeader(); await fileLibrariesPage.dataTable.doubleClickOnRowByName(siteName); await fileLibrariesPage.dataTable.waitForHeader(); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); done(); }); @@ -462,8 +462,8 @@ describe('Mark items as favorites', () => { it('Favorite a folder - [C280391]', async () => { await dataTable.selectItem(folderSite); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Favorite'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); await apis.user.favorites.waitForApi({ expect: 3 }); const isFavorite = await apis.user.favorites.isFavorite(folderSiteId); expect(isFavorite).toBe(true, `${folderSite} not marked as favorite`); @@ -473,8 +473,8 @@ describe('Mark items as favorites', () => { it('Favorite a file - [C280342]', async () => { await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderSite); await dataTable.selectItem(fileSiteNotFav1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Favorite'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); await apis.user.favorites.waitForApi({ expect: 3 }); const isFavorite = await apis.user.favorites.isFavorite(fileSiteNotFav1Id); expect(isFavorite).toBe(true, `${fileSiteNotFav1} not marked as favorite`); @@ -484,8 +484,8 @@ describe('Mark items as favorites', () => { it('Unfavorite an item - [C280343]', async () => { await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderSite); await dataTable.selectItem(fileSiteFav1); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Favorite'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); await apis.user.favorites.waitForApi({ expect: 3 }); const isFavorite = await apis.user.favorites.isFavorite(fileSiteFav1Id); expect(isFavorite).toBe(false, `${fileSiteFav1} is marked as favorite`); @@ -495,8 +495,8 @@ describe('Mark items as favorites', () => { it('Favorite multiple items - all unfavorite - [C280345]', async () => { await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderSite); await dataTable.selectMultipleItems([ fileSiteNotFav1, fileSiteNotFav2 ]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Favorite'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); await apis.user.favorites.waitForApi({ expect: 4 }); const listItems1 = await Promise.all([ apis.user.favorites.isFavorite(fileSiteNotFav1Id), @@ -511,8 +511,8 @@ describe('Mark items as favorites', () => { it('Unfavorite multiple items - [C280346]', async () => { await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderSite); await dataTable.selectMultipleItems([ fileSiteFav1, fileSiteFav2 ]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Favorite'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); const listItems2 = await Promise.all([ apis.user.favorites.isFavorite(fileSiteFav1Id), apis.user.favorites.isFavorite(fileSiteFav2Id) @@ -526,8 +526,8 @@ describe('Mark items as favorites', () => { it('Favorite multiple items - some favorite and some unfavorite - [C280347]', async () => { await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderSite); await dataTable.selectMultipleItems([ fileSiteNotFav1, fileSiteFav1 ]); - await toolbar.actions.openMoreMenu(); - await toolbar.actions.menu.clickMenuItem('Favorite'); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Favorite'); await apis.user.favorites.waitForApi({ expect: 3 }); const listItems3 = await Promise.all([ apis.user.favorites.isFavorite(fileSiteNotFav1Id), diff --git a/e2e/suites/actions/permanently-delete.test.ts b/e2e/suites/actions/permanently-delete.test.ts index 6d02be47d..f3e740e24 100755 --- a/e2e/suites/actions/permanently-delete.test.ts +++ b/e2e/suites/actions/permanently-delete.test.ts @@ -81,7 +81,7 @@ describe('Permanently delete from Trash', () => { it('delete file - [C217091]', async () => { await dataTable.selectItem(file1); - await toolbar.actions.getButtonByTitleAttribute('Permanently delete').click(); + await toolbar.getButtonByTitleAttribute('Permanently delete').click(); await trashPage.waitForDialog(); await trashPage.getDialogActionByLabel('Delete').click(); await trashPage.waitForDialogToClose(); @@ -93,7 +93,7 @@ describe('Permanently delete from Trash', () => { it('delete folder - [C280416]', async () => { await dataTable.selectItem(folder1); - await toolbar.actions.getButtonByTitleAttribute('Permanently delete').click(); + await toolbar.getButtonByTitleAttribute('Permanently delete').click(); await trashPage.waitForDialog(); await trashPage.getDialogActionByLabel('Delete').click(); await trashPage.waitForDialogToClose(); @@ -105,7 +105,7 @@ describe('Permanently delete from Trash', () => { it('delete multiple items - [C280417]', async () => { await dataTable.selectMultipleItems([ file2, folder2 ]); - await toolbar.actions.getButtonByTitleAttribute('Permanently delete').click(); + await toolbar.getButtonByTitleAttribute('Permanently delete').click(); await trashPage.waitForDialog(); await trashPage.getDialogActionByLabel('Delete').click(); await trashPage.waitForDialogToClose(); @@ -118,7 +118,7 @@ describe('Permanently delete from Trash', () => { it('Confirmation dialog UI - [C269113]', async () => { await dataTable.selectItem(file3); - await toolbar.actions.getButtonByTitleAttribute('Permanently delete').click(); + await toolbar.getButtonByTitleAttribute('Permanently delete').click(); await trashPage.waitForDialog(); expect(await confirmDialog.getTitle()).toContain('Delete from trash'); @@ -132,7 +132,7 @@ describe('Permanently delete from Trash', () => { it('"Keep" action cancels the deletion - [C269115]', async () => { await dataTable.selectItem(file3); - await toolbar.actions.getButtonByTitleAttribute('Permanently delete').click(); + await toolbar.getButtonByTitleAttribute('Permanently delete').click(); await trashPage.waitForDialog(); expect(await confirmDialog.keepButton.isEnabled()).toBe(true, 'KEEP button is not enabled'); diff --git a/e2e/suites/actions/restore.test.ts b/e2e/suites/actions/restore.test.ts index 4de3137ff..44591fa73 100755 --- a/e2e/suites/actions/restore.test.ts +++ b/e2e/suites/actions/restore.test.ts @@ -81,7 +81,7 @@ describe('Restore from Trash', () => { it('restore file - [C217177]', () => { dataTable.selectItem(file) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => { expect(text).toContain(`${file} restored`); @@ -99,7 +99,7 @@ describe('Restore from Trash', () => { it('restore folder - [C280438]', () => { dataTable.selectItem(folder) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => { expect(text).toContain(`${folder} restored`); @@ -117,7 +117,7 @@ describe('Restore from Trash', () => { it('restore multiple items - [C217182]', () => { dataTable.selectMultipleItems([ file, folder ]) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => { expect(text).toContain(`Restore successful`); @@ -137,7 +137,7 @@ describe('Restore from Trash', () => { it('View from notification - [C217181]', () => { dataTable.selectItem(file) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.clickSnackBarAction()) .then(() => page.dataTable.waitForHeader()) .then(() => { @@ -187,7 +187,7 @@ describe('Restore from Trash', () => { it('Restore a file when another file with same name exists on the restore location - [C217178]', () => { page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH) .then(() => dataTable.selectItem(file1)) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => expect(text).toEqual(`Can't restore, ${file1} already exists`)); }); @@ -195,7 +195,7 @@ describe('Restore from Trash', () => { it('Restore a file when original location no longer exists - [C217179]', () => { page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH) .then(() => dataTable.selectItem(file2)) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => expect(text).toEqual(`Can't restore ${file2}, the original location no longer exists`)); }); @@ -253,14 +253,14 @@ describe('Restore from Trash', () => { it('one failure - [C217183]', () => { dataTable.selectMultipleItems([ file1, file2 ]) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => expect(text).toEqual(`Can't restore ${file1}, the original location no longer exists`)); }); it('multiple failures - [C217184]', () => { dataTable.selectMultipleItems([ file3, file4, file5 ]) - .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) + .then(() => toolbar.getButtonByTitleAttribute('Restore').click()) .then(() => page.getSnackBarMessage()) .then(text => expect(text).toEqual('2 items not restored because of issues with the restore location')); }); diff --git a/e2e/suites/actions/single-click.test.ts b/e2e/suites/actions/single-click.test.ts index 54fea644d..c4ac46ace 100755 --- a/e2e/suites/actions/single-click.test.ts +++ b/e2e/suites/actions/single-click.test.ts @@ -50,8 +50,7 @@ describe('Single click on item name', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const page = new BrowsingPage(); - const { dataTable } = page; - const { breadcrumb } = page.toolbar; + const { dataTable, breadcrumb } = page; const viewer = new Viewer(); beforeAll(async (done) => { diff --git a/e2e/suites/actions/special-permissions-available-actions.test.ts b/e2e/suites/actions/special-permissions-available-actions.test.ts index 150d1c2df..e4b7ab100 100755 --- a/e2e/suites/actions/special-permissions-available-actions.test.ts +++ b/e2e/suites/actions/special-permissions-available-actions.test.ts @@ -106,46 +106,46 @@ describe('Granular permissions available actions : ', () => { await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Shared Files - [C280477]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Favorites - [C280478]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); // TODO: enable when ACA-1737 is done // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -161,51 +161,51 @@ describe('Granular permissions available actions : ', () => { await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectItem(file1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); - const menu = await toolbar.actions.openMoreMenu(); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Shared Files - [C280456]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); await page.dataTable.waitForHeader(); await page.dataTable.selectItem(file1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Favorites - [C213121]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); await dataTable.selectItem(file1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${file1}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${file1}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${file1}`); // TODO: enable when ACA-1737 is done // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`); // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${file1}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -221,35 +221,35 @@ describe('Granular permissions available actions : ', () => { await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectItem(folder1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folder1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); - expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${folder1}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); - const menu = await toolbar.actions.openMoreMenu(); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folder1}`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folder1}`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folder1}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folder1}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Favorites - [C286266]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); await dataTable.selectItem(folder1); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is not displayed for ${folder1}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); - expect(await toolbar.actions.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is not displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`); + expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${folder1}`); // TODO: enable when ACA-1737 is done - // expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); - const menu = await toolbar.actions.openMoreMenu(); + // expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folder1}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folder1}`); // TODO: enable when ACA-1737 is done // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folder1}`); // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folder1}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folder1}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -266,46 +266,46 @@ describe('Granular permissions available actions : ', () => { await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Shared Files - [C286284]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Favorites - [C286285]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, file2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); // TODO: enable when ACA-1737 is done // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -322,31 +322,31 @@ describe('Granular permissions available actions : ', () => { await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ folder1, folder2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Favorites - [C286286]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ folder1, folder2 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); // TODO: enable when ACA-1737 is done // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -363,31 +363,31 @@ describe('Granular permissions available actions : ', () => { await dataTable.doubleClickOnRowByName(siteName); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, folder1 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`); expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`); expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('on Favorites - [C286287]', async () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); await dataTable.waitForHeader(); await dataTable.selectMultipleItems([ file1, folder1 ]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for selected files`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); // TODO: enable when ACA-1737 is done // expect(await menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for selected files`); // expect(await menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); diff --git a/e2e/suites/actions/toolbar-multiple-selection.test.ts b/e2e/suites/actions/toolbar-multiple-selection.test.ts index 0e57962b0..4682bb100 100755 --- a/e2e/suites/actions/toolbar-multiple-selection.test.ts +++ b/e2e/suites/actions/toolbar-multiple-selection.test.ts @@ -135,41 +135,41 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple files are selected - [C217112]', async () => { await dataTable.selectMultipleItems([file1, file2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('correct actions appear when multiple folders are selected - [C280459]', async () => { await dataTable.selectMultipleItems([folder1, folder2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('correct actions appear when both files and folders are selected - [C280460]', async () => { await dataTable.selectMultipleItems([file1, file2, folder1, folder2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for selected files`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -186,10 +186,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple files are selected - [C280461]', async () => { await dataTable.selectMultipleItems([file1InSite, file2InSite]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed for selected files'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -198,10 +198,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple folders are selected - [C280462]', async () => { await dataTable.selectMultipleItems([folder1InSite, folder2InSite]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -210,10 +210,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when both files and folders are selected - [C280463]', async () => { await dataTable.selectMultipleItems([file1InSite, file2InSite, folder1InSite, folder2InSite]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -232,10 +232,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple files are selected - [C280467]', async () => { await dataTable.selectMultipleItems([file1, file2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed for selected files'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -254,10 +254,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple files are selected - [C280468]', async () => { await dataTable.selectMultipleItems([file1, file2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -276,10 +276,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple files are selected - [C280469]', async () => { await dataTable.selectMultipleItems([file1, file2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -288,10 +288,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple folders are selected - [C280470]', async () => { await dataTable.selectMultipleItems([folder1, folder2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -300,10 +300,10 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when both files and folders are selected - [C280471]', async () => { await dataTable.selectMultipleItems([file1, file2, folder1, folder2]); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, 'View is displayed'); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isButtonPresent('View')).toBe(false, 'View is displayed'); + expect(await toolbar.isButtonPresent('Download')).toBe(true, 'Download is not displayed for selected files'); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, 'Edit is displayed'); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for selected files`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for selected files`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for selected files`); @@ -321,20 +321,20 @@ describe('Toolbar actions - multiple selection : ', () => { it('correct actions appear when multiple files are selected - [C280472]', async () => { await dataTable.selectMultipleItems([fileForDelete1, fileForDelete2]); - expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); + expect(await toolbar.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); + expect(await toolbar.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); }); it('correct actions appear when multiple folders are selected - [C280473]', async () => { await dataTable.selectMultipleItems([folderForDelete1, folderForDelete2]); - expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); + expect(await toolbar.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); + expect(await toolbar.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); }); it('correct actions appear when both files and folders are selected - [C280474]', async () => { await dataTable.selectMultipleItems([fileForDelete1, fileForDelete2, folderForDelete1, folderForDelete2]); - expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); + expect(await toolbar.isButtonPresent('Permanently delete')).toBe(true, 'Permanently delete is displayed'); + expect(await toolbar.isButtonPresent('Restore')).toBe(true, 'Restore is not displayed'); }); }); }); diff --git a/e2e/suites/actions/toolbar-single-selection.test.ts b/e2e/suites/actions/toolbar-single-selection.test.ts index 4f3c2de25..89591cdcc 100755 --- a/e2e/suites/actions/toolbar-single-selection.test.ts +++ b/e2e/suites/actions/toolbar-single-selection.test.ts @@ -97,7 +97,7 @@ describe('Toolbar actions - single selection : ', () => { await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); await dataTable.waitForHeader(); await dataTable.selectItem(siteName); - expect(await toolbar.actions.isEmpty()).toBe(true, 'toolbar not empty'); + expect(await toolbar.isEmpty()).toBe(true, 'toolbar not empty'); }); it('selected row is marked with a check circle icon - [C213134]', async () => { @@ -118,35 +118,35 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are not displayed when no item is selected - [C213120]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + expect(await toolbar.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); it('correct actions appear when a file is selected - [C213122]', async () => { await dataTable.selectItem(fileUser); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('correct actions appear when a folder is selected - [C213123]', async () => { await dataTable.selectItem(folderUser); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderUser}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderUser}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderUser}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderUser}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -161,35 +161,35 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are not displayed when no item is selected - [C280439]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + expect(await toolbar.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); it('correct actions appear when a file is selected - [C280440]', async () => { await dataTable.selectItem(fileInSite); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileInSite}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileInSite}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileInSite}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileInSite}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${fileInSite}`); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileInSite}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileInSite}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileInSite}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileInSite}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileInSite}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileInSite}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileInSite}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('correct actions appear when a folder is selected - [C280441]', async () => { await dataTable.selectItem(folderInSite); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderInSite}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderInSite}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderInSite}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderInSite}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${folderInSite}`); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for ${folderInSite}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderInSite}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderInSite}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderInSite}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderInSite}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderInSite}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderInSite}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -202,21 +202,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are not displayed when no item is selected - [C280445]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + expect(await toolbar.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); it('correct actions appear when a file is selected - [C286265]', async () => { await page.dataTable.selectItem(fileUser); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -230,21 +230,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are not displayed when no item is selected - [C280447]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + expect(await toolbar.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); it('correct actions appear when a file is selected - [C280448]', async () => { await dataTable.selectItem(fileUser); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -257,35 +257,35 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are not displayed when no item is selected - [C280449]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + expect(await toolbar.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); it('correct actions appear when a file is selected - [C280450]', async () => { await dataTable.selectItem(fileUser); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); it('correct actions appear when a folder is selected - [C280451]', async () => { await dataTable.selectItem(folderUser); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); - expect(await toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); - expect(await toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); - expect(await toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); - const menu = await toolbar.actions.openMoreMenu(); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); + expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); + expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); + expect(await toolbar.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + const menu = await toolbar.openMoreMenu(); expect(await menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderUser}`); expect(await menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderUser}`); expect(await menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderUser}`); expect(await menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderUser}`); - await toolbar.actions.closeMoreMenu(); + await toolbar.closeMoreMenu(); }); }); @@ -298,21 +298,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are not displayed when no item is selected - [C280452]', async () => { - expect(await toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + expect(await toolbar.isEmpty()).toBe(true, `actions displayed though nothing selected`); }); it('correct actions appear when a file is selected - [C280453]', async () => { await dataTable.selectItem(fileForDelete); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileForDelete}`); - expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, `Permanently delete is not displayed for file`); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, `Restore is not displayed for file`); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${fileForDelete}`); + expect(await toolbar.isButtonPresent('Permanently delete')).toBe(true, `Permanently delete is not displayed for file`); + expect(await toolbar.isButtonPresent('Restore')).toBe(true, `Restore is not displayed for file`); }); it('correct actions appear when a folder is selected - [C280454]', async () => { await dataTable.selectItem(folderForDelete); - expect(await toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderForDelete}`); - expect(await toolbar.actions.isButtonPresent('Permanently delete')).toBe(true, `Permanently delete is displayed for folder`); - expect(await toolbar.actions.isButtonPresent('Restore')).toBe(true, `Restore is not enabled for folder`); + expect(await toolbar.isEmpty()).toBe(false, `actions not displayed for ${folderForDelete}`); + expect(await toolbar.isButtonPresent('Permanently delete')).toBe(true, `Permanently delete is displayed for folder`); + expect(await toolbar.isButtonPresent('Restore')).toBe(true, `Restore is not enabled for folder`); }); }); }); diff --git a/e2e/suites/extensions/ext-info-drawer.test.ts b/e2e/suites/extensions/ext-info-drawer.test.ts index 881774728..cdbb349d1 100755 --- a/e2e/suites/extensions/ext-info-drawer.test.ts +++ b/e2e/suites/extensions/ext-info-drawer.test.ts @@ -101,7 +101,7 @@ describe('Extensions - Info Drawer', () => { it('Add a new tab with icon and title - [C284646]', async () => { await page.dataTable.selectItem(file); - await page.toolbar.actions.getButtonByTitleAttribute('View details').click(); + await page.toolbar.getButtonByTitleAttribute('View details').click(); await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isTabPresent(custom_tab.title)).toBe(true, `${custom_tab.title} tab is not present`); @@ -110,7 +110,7 @@ describe('Extensions - Info Drawer', () => { it('Remove existing tab - [C284647]', async () => { await page.dataTable.selectItem(file); - await page.toolbar.actions.getButtonByTitleAttribute('View details').click(); + await page.toolbar.getButtonByTitleAttribute('View details').click(); await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isTabPresent(comments_tab.title)).toBe(false, `${comments_tab.title} tab should not be present!`); @@ -118,7 +118,7 @@ describe('Extensions - Info Drawer', () => { it('Change tab title - [C284648]', async () => { await page.dataTable.selectItem(file); - await page.toolbar.actions.getButtonByTitleAttribute('View details').click(); + await page.toolbar.getButtonByTitleAttribute('View details').click(); await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isTabPresent(properties_tab.title)).toBe(true, `${properties_tab.title} tab is not present`); @@ -127,7 +127,7 @@ describe('Extensions - Info Drawer', () => { it('Tab with icon and no title - [C284649]', async () => { await page.dataTable.selectItem(file); - await page.toolbar.actions.getButtonByTitleAttribute('View details').click(); + await page.toolbar.getButtonByTitleAttribute('View details').click(); await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isTabPresent(no_title_tab.title)).toBe(true, `${no_title_tab.title} tab is not present`); @@ -136,7 +136,7 @@ describe('Extensions - Info Drawer', () => { it('Insert new component in tab - [C284651]', async () => { await page.dataTable.selectItem(file); - await page.toolbar.actions.getButtonByTitleAttribute('View details').click(); + await page.toolbar.getButtonByTitleAttribute('View details').click(); await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isTabDisplayed(custom_tab.title)).toBe(true, `${custom_tab.title} tab is not displayed`); @@ -162,7 +162,7 @@ describe('Extensions - Info Drawer', () => { it('Remove all tabs - [C284650]', async () => { await page.dataTable.selectItem(file); - await page.toolbar.actions.getButtonByTitleAttribute('View details').click(); + await page.toolbar.getButtonByTitleAttribute('View details').click(); await infoDrawer.waitForInfoDrawerToOpen(); expect(await infoDrawer.isEmpty()).toBe(true, 'Info Drawer is not empty'); diff --git a/e2e/suites/list-views/favorites.test.ts b/e2e/suites/list-views/favorites.test.ts index b582dcf3b..4923a3d8d 100755 --- a/e2e/suites/list-views/favorites.test.ts +++ b/e2e/suites/list-views/favorites.test.ts @@ -48,8 +48,7 @@ describe('Favorites', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const favoritesPage = new BrowsingPage(); - const { dataTable } = favoritesPage; - const { breadcrumb } = favoritesPage.toolbar; + const { dataTable, breadcrumb } = favoritesPage; beforeAll(async (done) => { await apis.admin.people.createUser({ username }); diff --git a/e2e/suites/list-views/recent-files.test.ts b/e2e/suites/list-views/recent-files.test.ts index 3fa15c98e..3a273acf0 100755 --- a/e2e/suites/list-views/recent-files.test.ts +++ b/e2e/suites/list-views/recent-files.test.ts @@ -48,8 +48,7 @@ describe('Recent Files', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const recentFilesPage = new BrowsingPage(); - const { dataTable } = recentFilesPage; - const { breadcrumb } = recentFilesPage.toolbar; + const { dataTable, breadcrumb } = recentFilesPage; beforeAll(done => { apis.admin.people.createUser({ username }) diff --git a/e2e/suites/list-views/shared-files.test.ts b/e2e/suites/list-views/shared-files.test.ts index 0df169011..2b0f14b0c 100755 --- a/e2e/suites/list-views/shared-files.test.ts +++ b/e2e/suites/list-views/shared-files.test.ts @@ -49,8 +49,7 @@ describe('Shared Files', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const sharedFilesPage = new BrowsingPage(); - const { dataTable } = sharedFilesPage; - const { breadcrumb } = sharedFilesPage.toolbar; + const { dataTable, breadcrumb } = sharedFilesPage; beforeAll(done => { apis.admin.people.createUser({ username }) diff --git a/e2e/suites/list-views/trash.test.ts b/e2e/suites/list-views/trash.test.ts index 23f9e3be6..17214511b 100755 --- a/e2e/suites/list-views/trash.test.ts +++ b/e2e/suites/list-views/trash.test.ts @@ -55,8 +55,7 @@ describe('Trash', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const trashPage = new BrowsingPage(); - const { dataTable } = trashPage; - const { breadcrumb } = trashPage.toolbar; + const { dataTable, breadcrumb } = trashPage; beforeAll(done => { apis.admin.people.createUser({ username }) diff --git a/e2e/suites/navigation/breadcrumb.test.ts b/e2e/suites/navigation/breadcrumb.test.ts index dc73de2d7..e7aefdf8f 100755 --- a/e2e/suites/navigation/breadcrumb.test.ts +++ b/e2e/suites/navigation/breadcrumb.test.ts @@ -47,7 +47,7 @@ describe('Breadcrumb', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const page = new BrowsingPage(); - const { breadcrumb } = page.toolbar; + const { breadcrumb } = page; const apis = { admin: new RepoClient(), diff --git a/e2e/suites/viewer/viewer-general.test.ts b/e2e/suites/viewer/viewer-general.test.ts index c8a95bad7..403b4e1e7 100755 --- a/e2e/suites/viewer/viewer-general.test.ts +++ b/e2e/suites/viewer/viewer-general.test.ts @@ -90,14 +90,13 @@ describe('Viewer general', () => { }); afterAll(async (done) => { - await Promise - .all([ - apis.user.nodes.deleteNodeById(parentId), - apis.admin.sites.deleteSite(siteAdmin), - apis.user.sites.deleteSite(siteUser), - logoutPage.load() - ]) - .then(done); + await Promise.all([ + apis.user.nodes.deleteNodeById(parentId), + apis.admin.sites.deleteSite(siteAdmin), + apis.user.sites.deleteSite(siteUser), + logoutPage.load() + ]); + done(); }); it('Viewer opens on double clicking on a file from Personal Files - [C279269]', async () => { @@ -107,7 +106,7 @@ describe('Viewer general', () => { it('Viewer opens when clicking the View action for a file - [C279270]', async () => { await dataTable.selectItem(xlsxFile); - await page.toolbar.actions.getButtonByTitleAttribute('View').click(); + await page.toolbar.getButtonByTitleAttribute('View').click(); expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); }); From 6e98721a623c66489da6b672e7540501dfff7bee Mon Sep 17 00:00:00 2001 From: Suzana Dirla Date: Thu, 13 Sep 2018 11:05:55 +0300 Subject: [PATCH 010/160] [ACA-1569] Close dialogs on logout (#621) * Close dialogs on logout * [ACA-1569] e2e tests * [ACA-1569] review required change on e2e test --- e2e/suites/application/general.test.ts | 84 +++++++++++++++++++ .../apis/authentication/authentication-api.ts | 38 +++++++++ e2e/utilities/repo-client/repo-client.ts | 5 ++ protractor.conf.js | 2 +- src/app/app.component.ts | 6 +- 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 e2e/suites/application/general.test.ts create mode 100755 e2e/utilities/repo-client/apis/authentication/authentication-api.ts diff --git a/e2e/suites/application/general.test.ts b/e2e/suites/application/general.test.ts new file mode 100644 index 000000000..3ba41229e --- /dev/null +++ b/e2e/suites/application/general.test.ts @@ -0,0 +1,84 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser } from 'protractor'; +import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages'; +import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('General', () => { + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const createDialog = new CreateOrEditFolderDialog(); + const adminApi = new RepoClient(); + const { nodes: nodesApi, authentication: authApi } = adminApi; + const folder = `folder-${Utils.random()}`; + let folderId; + xit(''); + + describe('on session expire', () => { + beforeAll(async () => { + folderId = (await nodesApi.createFolder(folder)).entry.id; + }); + + afterAll(async () => { + await nodesApi.deleteNodeById(folderId); + await logoutPage.load(); + }); + + it('should redirect user to login page' , async () => { + await loginPage.loginWithAdmin(); + + await page.sidenav.openCreateDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(folder); + + await authApi.logout(); + + await createDialog.clickCreate(); + expect(await browser.getTitle()).toContain('Sign in'); + }); + + it('should close opened dialogs', async () => { + await loginPage.loginWithAdmin(); + + await page.sidenav.openCreateDialog(); + await createDialog.waitForDialogToOpen(); + await createDialog.enterName(folder); + + await authApi.logout(); + + await createDialog.clickCreate(); + const message = await page.getSnackBarMessage(); + expect(message).toEqual('The action was unsuccessful. Try again or contact your IT Team.'); + + expect(createDialog.component.isPresent()).not.toBe(true, 'dialog is present'); + }); + + }); + +}); diff --git a/e2e/utilities/repo-client/apis/authentication/authentication-api.ts b/e2e/utilities/repo-client/apis/authentication/authentication-api.ts new file mode 100755 index 000000000..e7466c124 --- /dev/null +++ b/e2e/utilities/repo-client/apis/authentication/authentication-api.ts @@ -0,0 +1,38 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { RepoApi } from '../repo-api'; + +export class AuthenticationApi extends RepoApi { + + constructor(username?, password?) { + super(username, password); + } + + async logout() { + await this.apiAuth(); + return await this.alfrescoJsApi.logout(); + } +} diff --git a/e2e/utilities/repo-client/repo-client.ts b/e2e/utilities/repo-client/repo-client.ts index b8b708df8..51e814231 100755 --- a/e2e/utilities/repo-client/repo-client.ts +++ b/e2e/utilities/repo-client/repo-client.ts @@ -33,6 +33,7 @@ import { SharedLinksApi } from './apis/shared-links/shared-links-api'; import { TrashcanApi } from './apis/trashcan/trashcan-api'; import { SearchApi } from './apis/search/search-api'; import { UploadApi } from './apis/upload/upload-api'; +import { AuthenticationApi } from './apis/authentication/authentication-api'; export class RepoClient { constructor( @@ -76,6 +77,10 @@ export class RepoClient { get upload() { return new UploadApi(this.auth.username, this.auth.password); } + + get authentication() { + return new AuthenticationApi(this.auth.username, this.auth.password); + } } export * from './apis/nodes/node-body-create'; diff --git a/protractor.conf.js b/protractor.conf.js index e6e81dbce..fdd13b802 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -16,7 +16,7 @@ exports.config = { specs: [ './e2e/suites/authentication/*.test.ts', './e2e/suites/list-views/*.test.ts', - './e2e/suites/application/page-titles.test.ts', + './e2e/suites/application/*.test.ts', './e2e/suites/navigation/*.test.ts', './e2e/suites/pagination/*.test.ts', './e2e/suites/actions/*.test.ts', diff --git a/src/app/app.component.ts b/src/app/app.component.ts index dae38a1e0..780f0e181 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -43,6 +43,7 @@ import { } from './store/actions'; import { AppStore, AppState, INITIAL_APP_STATE } from './store/states/app.state'; import { filter } from 'rxjs/operators'; +import { MatDialog } from '@angular/material'; @Component({ selector: 'app-root', @@ -59,7 +60,8 @@ export class AppComponent implements OnInit { private alfrescoApiService: AlfrescoApiService, private authenticationService: AuthenticationService, private uploadService: UploadService, - private extensions: AppExtensionService + private extensions: AppExtensionService, + private dialogRef: MatDialog ) {} ngOnInit() { @@ -68,6 +70,8 @@ export class AppComponent implements OnInit { if (!this.authenticationService.isLoggedIn()) { this.authenticationService.setRedirect({ provider: 'ECM', url: this.router.url }); this.router.navigate(['/login']); + + this.dialogRef.closeAll(); } } }); From 4af9628b9b194d70562b214d180114fef4b3a81f Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Thu, 13 Sep 2018 10:31:51 +0100 Subject: [PATCH 011/160] unit tests (#627) * evaluator tests * extension service tests * cleanup scripts --- angular.json | 13 +- package.json | 15 +- projects/aca-dev-tools/ng-package.prod.json | 7 - projects/adf-extensions/karma.conf.js | 13 +- projects/adf-extensions/ng-package.prod.json | 8 - .../lib/evaluators/core.evaluators.spec.ts | 237 +++++++++++ .../src/lib/evaluators/core.evaluators.ts | 3 + .../lib/services/extension.service.spec.ts | 379 ++++++++++++++++++ .../src/lib/services/extension.service.ts | 2 +- 9 files changed, 641 insertions(+), 36 deletions(-) delete mode 100644 projects/aca-dev-tools/ng-package.prod.json delete mode 100644 projects/adf-extensions/ng-package.prod.json create mode 100644 projects/adf-extensions/src/lib/evaluators/core.evaluators.spec.ts create mode 100644 projects/adf-extensions/src/lib/services/extension.service.spec.ts diff --git a/angular.json b/angular.json index 845fb2db8..a65a26ee6 100644 --- a/angular.json +++ b/angular.json @@ -219,11 +219,6 @@ "options": { "tsConfig": "projects/aca-dev-tools/tsconfig.lib.json", "project": "projects/aca-dev-tools/ng-package.json" - }, - "configurations": { - "production": { - "project": "projects/aca-dev-tools/ng-package.prod.json" - } } }, "test": { @@ -259,11 +254,6 @@ "options": { "tsConfig": "projects/adf-extensions/tsconfig.lib.json", "project": "projects/adf-extensions/ng-package.json" - }, - "configurations": { - "production": { - "project": "projects/adf-extensions/ng-package.prod.json" - } } }, "test": { @@ -271,7 +261,8 @@ "options": { "main": "projects/adf-extensions/src/test.ts", "tsConfig": "projects/adf-extensions/tsconfig.spec.json", - "karmaConfig": "projects/adf-extensions/karma.conf.js" + "karmaConfig": "projects/adf-extensions/karma.conf.js", + "sourceMap": true } }, "lint": { diff --git a/package.json b/package.json index 8ea7562ed..52bbd2310 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,14 @@ "license": "LGPL-3.0", "scripts": { "ng": "ng", - "start": "npm run server-versions && npm run build.libs.dev && ng serve --open", - "start:prod": "npm run server-versions && npm run build.libs.prod && ng serve --prod --open", - "build.libs.dev": "ng build adf-extensions && ng build aca-dev-tools && npm run build:my-extension", - "build.libs.prod": "ng build adf-extensions --prod && ng build aca-dev-tools --prod && npm run build:my-extension", - "build": "npm run server-versions && npm run build.libs.prod && node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod", - "build:dev": "npm run server-versions && npm run build.libs.dev && ng build", + "start": "npm run server-versions && npm run build.libs && ng serve --open", + "start:prod": "npm run server-versions && npm run build.libs && ng serve --prod --open", + "build.libs": "ng build adf-extensions && ng build aca-dev-tools && npm run build:my-extension", + "build": "npm run server-versions && npm run build.libs && node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app --prod", + "build:dev": "npm run server-versions && npm run build.libs && ng build", "build:my-extension": "ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst", - "test": "npm run build.libs.dev && ng test app --code-coverage", - "test:ci": "npm run build.libs.dev && ng test app --code-coverage --watch=false", + "test": "npm run build.libs && ng test app --code-coverage", + "test:ci": "npm run build.libs && ng test app --code-coverage --watch=false", "lint": "ng lint", "server-versions": "rimraf ./src/versions.json && npm list --depth=0 --json=true --prod=true > ./src/versions.json || exit 0", "__e2e": "ng e2e", diff --git a/projects/aca-dev-tools/ng-package.prod.json b/projects/aca-dev-tools/ng-package.prod.json deleted file mode 100644 index 7a9f7d515..000000000 --- a/projects/aca-dev-tools/ng-package.prod.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/aca-dev-tools", - "lib": { - "entryFile": "src/public_api.ts" - } -} \ No newline at end of file diff --git a/projects/adf-extensions/karma.conf.js b/projects/adf-extensions/karma.conf.js index 4c5f8d03f..f43d65fff 100644 --- a/projects/adf-extensions/karma.conf.js +++ b/projects/adf-extensions/karma.conf.js @@ -25,7 +25,18 @@ module.exports = function (config) { colors: true, logLevel: config.LOG_INFO, autoWatch: true, - browsers: ['Chrome'], + browsers: [/*'Chrome',*/ 'ChromeHeadless'], + customLaunchers: { + ChromeHeadless: { + base: 'Chrome', + flags: [ + '--no-sandbox', + '--headless', + '--disable-gpu', + '--remote-debugging-port=9222' + ] + } + }, singleRun: false }); }; diff --git a/projects/adf-extensions/ng-package.prod.json b/projects/adf-extensions/ng-package.prod.json deleted file mode 100644 index 48af0c7aa..000000000 --- a/projects/adf-extensions/ng-package.prod.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/@alfresco/adf-extensions", - "lib": { - "languageLevel": ["dom", "es2017"], - "entryFile": "src/public_api.ts" - } -} diff --git a/projects/adf-extensions/src/lib/evaluators/core.evaluators.spec.ts b/projects/adf-extensions/src/lib/evaluators/core.evaluators.spec.ts new file mode 100644 index 000000000..304be9919 --- /dev/null +++ b/projects/adf-extensions/src/lib/evaluators/core.evaluators.spec.ts @@ -0,0 +1,237 @@ +/*! + * @license + * Copyright 2016 - 2018 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 { every, not, some } from './core.evaluators'; +import { RuleParameter } from '../config/rule.extensions'; + +describe('Core Evaluators', () => { + + const context: any = { + getEvaluator(key: string) { + switch (key) { + case 'positive': + return () => true; + case 'negative': + return () => false; + default: + return null; + } + } + }; + + describe('not', () => { + it('should evaluate a single rule to [true]', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const result = not(context, parameter); + expect(result).toBeTruthy(); + }); + + it('should evaluate to [false] when no parameters provided', () => { + const result = not(context); + expect(result).toBeFalsy(); + }); + + it('should evaluate to [false] when evaluator not available', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'missing' + }; + + const result = not(context, parameter); + expect(result).toBeFalsy(); + }); + + it('should evaluate a single rule to [false]', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const result = not(context, parameter); + expect(result).toBeFalsy(); + }); + + it('should evaluate multiple rules to [true]', () => { + const parameter1: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const parameter2: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const parameter3: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const result = not(context, parameter1, parameter2, parameter3); + expect(result).toBeTruthy(); + }); + + it('should evaluate to [false] when one of the rules fails', () => { + const parameter1: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const parameter2: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const parameter3: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const result = not(context, parameter1, parameter2, parameter3); + expect(result).toBeFalsy(); + }); + }); + + describe('every', () => { + it('should evaluate a single rule to [true]', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const result = every(context, parameter); + expect(result).toBeTruthy(); + }); + + it('should evaluate to [false] when no parameters provided', () => { + const result = every(context); + expect(result).toBeFalsy(); + }); + + it('should evaluate to [false] when evaluator not available', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'missing' + }; + + const result = every(context, parameter); + expect(result).toBeFalsy(); + }); + + it('should evaluate a single rule to [false]', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const result = every(context, parameter); + expect(result).toBeFalsy(); + }); + + it('should evaluate multiple rules to [true]', () => { + const parameter1: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const parameter2: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const parameter3: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const result = every(context, parameter1, parameter2, parameter3); + expect(result).toBeTruthy(); + }); + + it('should evaluate to [false] when one of the rules fails', () => { + const parameter1: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const parameter2: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const parameter3: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const result = every(context, parameter1, parameter2, parameter3); + expect(result).toBeFalsy(); + }); + }); + + describe('some', () => { + it('should evaluate a single rule to [true]', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const result = some(context, parameter); + expect(result).toBeTruthy(); + }); + + it('should evaluate to [false] when no parameters provided', () => { + const result = some(context); + expect(result).toBeFalsy(); + }); + + it('should evaluate to [false] when evaluator not available', () => { + const parameter: RuleParameter = { + type: 'primitive', + value: 'missing' + }; + + const result = some(context, parameter); + expect(result).toBeFalsy(); + }); + + it('should evaluate to [true] if any rule succeeds', () => { + const parameter1: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const parameter2: RuleParameter = { + type: 'primitive', + value: 'positive' + }; + + const parameter3: RuleParameter = { + type: 'primitive', + value: 'negative' + }; + + const result = some(context, parameter1, parameter2, parameter3); + expect(result).toBeTruthy(); + }); + }); + +}); diff --git a/projects/adf-extensions/src/lib/evaluators/core.evaluators.ts b/projects/adf-extensions/src/lib/evaluators/core.evaluators.ts index b52e71ee1..b6ce7748a 100644 --- a/projects/adf-extensions/src/lib/evaluators/core.evaluators.ts +++ b/projects/adf-extensions/src/lib/evaluators/core.evaluators.ts @@ -27,6 +27,7 @@ export function not(context: RuleContext, ...args: RuleParameter[]): boolean { const evaluator = context.getEvaluator(arg.value); if (!evaluator) { console.warn('evaluator not found: ' + arg.value); + return false; } return !evaluator(context, ...(arg.parameters || [])); }); @@ -42,6 +43,7 @@ export function every(context: RuleContext, ...args: RuleParameter[]): boolean { const evaluator = context.getEvaluator(arg.value); if (!evaluator) { console.warn('evaluator not found: ' + arg.value); + return false; } return evaluator(context, ...(arg.parameters || [])); }); @@ -57,6 +59,7 @@ export function some(context: RuleContext, ...args: RuleParameter[]): boolean { const evaluator = context.getEvaluator(arg.value); if (!evaluator) { console.warn('evaluator not found: ' + arg.value); + return false; } return evaluator(context, ...(arg.parameters || [])); }); diff --git a/projects/adf-extensions/src/lib/services/extension.service.spec.ts b/projects/adf-extensions/src/lib/services/extension.service.spec.ts new file mode 100644 index 000000000..701c68f95 --- /dev/null +++ b/projects/adf-extensions/src/lib/services/extension.service.spec.ts @@ -0,0 +1,379 @@ +/*! + * @license + * Copyright 2016 - 2018 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 { ExtensionService } from './extension.service'; +import { ExtensionLoaderService } from './extension-loader.service'; +import { ExtensionConfig } from '../config/extension.config'; +import { RuleRef } from '../config/rule.extensions'; +import { RouteRef } from '../config/routing.extensions'; +import { ActionRef } from '../config/action.extensions'; + +describe('ExtensionService', () => { + const blankConfig: ExtensionConfig = { + $name: 'test.config', + $version: '1.0.0' + }; + + let loader: ExtensionLoaderService; + let service: ExtensionService; + + beforeEach(() => { + loader = new ExtensionLoaderService(null); + service = new ExtensionService(loader); + }); + + it('should load and setup a config', async () => { + spyOn(loader, 'load').and.callFake(() => { + return Promise.resolve(blankConfig); + }); + + spyOn(service, 'setup').and.stub(); + + await service.load(); + + expect(loader.load).toHaveBeenCalled(); + expect(service.setup).toHaveBeenCalledWith(blankConfig); + }); + + it('should raise warning if setting up with missing config', () => { + spyOn(console, 'warn').and.stub(); + + service.setup(null); + + expect(console.warn).toHaveBeenCalledWith('Extension configuration not found'); + }); + + it('should setup default evaluators', () => { + service.setup(blankConfig); + + const evaluators = ['core.every', 'core.some', 'core.not']; + evaluators.forEach(key => { + expect(service.getEvaluator(key)).toBeDefined(`Evaluator ${key} is missing`); + }); + }); + + it('should set custom evaluators', () => { + const evaluator1 = () => true; + const evaluator2 = () => false; + + service.setEvaluators({ + 'eval1': evaluator1, + 'eval2': evaluator2 + }); + + expect(service.getEvaluator('eval1')).toBe(evaluator1); + expect(service.getEvaluator('eval2')).toBe(evaluator2); + }); + + it('should override existing evaluators', () => { + const evaluator1 = () => true; + const evaluator2 = () => false; + + service.setup(blankConfig); + expect(service.getEvaluator('core.every')).toBeDefined(); + expect(service.getEvaluator('core.every')).not.toBe(evaluator1); + + service.setEvaluators({ + 'core.every': evaluator1, + 'eval2': evaluator2 + }); + + expect(service.getEvaluator('core.every')).toBe(evaluator1); + expect(service.getEvaluator('eval2')).toBe(evaluator2); + }); + + it('should negate existing evaluator', () => { + const positive = () => true; + + service.setEvaluators({ + 'positive': positive + }); + + let evaluator = service.getEvaluator('positive'); + expect(evaluator(null)).toBe(true); + + evaluator = service.getEvaluator('!positive'); + expect(evaluator(null, 'param1', 'param2')).toBe(false); + }); + + it('should not update evaluators with null value', () => { + service.setup(blankConfig); + service.setEvaluators(null); + + expect(service.getEvaluator('core.every')).toBeDefined(); + }); + + it('should set authentication guards', () => { + let registered = service.getAuthGuards(['guard1']); + expect(registered.length).toBe(0); + + const guard1: any = {}; + const guard2: any = {}; + + service.setAuthGuards({ + 'auth1': guard1, + 'auth2': guard2 + }); + + registered = service.getAuthGuards(['auth1', 'auth2']); + expect(registered.length).toBe(2); + expect(registered[0]).toBe(guard1); + expect(registered[1]).toBe(guard2); + }); + + it('should overwrite authentication guards', () => { + const guard1: any = {}; + const guard2: any = {}; + + service.setAuthGuards({ + 'auth': guard1 + }); + + expect(service.getAuthGuards(['auth'])).toEqual([guard1]); + + service.setAuthGuards({ + 'auth': guard2 + }); + + expect(service.getAuthGuards(['auth'])).toEqual([guard2]); + }); + + it('should not set authentication guards with null value', () => { + const guard1: any = {}; + + service.setAuthGuards({ + 'auth': guard1 + }); + + service.setAuthGuards(null); + + expect(service.getAuthGuards(['auth'])).toEqual([guard1]); + }); + + it('should not fetch auth guards for missing ids', () => { + const guards = service.getAuthGuards(null); + expect(guards).toEqual([]); + }); + + it('should set components', () => { + const component: any = {}; + + service.setComponents({ + 'component1': component + }); + + expect(service.getComponentById('component1')).toBe(component); + }); + + it('should overwrite components', () => { + const component1: any = {}; + const component2: any = {}; + + service.setComponents({ + 'component': component1 + }); + + expect(service.getComponentById('component')).toBe(component1); + + service.setComponents({ + 'component': component2 + }); + + expect(service.getComponentById('component')).toBe(component2); + }); + + it('should not set components with null value', () => { + const component: any = {}; + + service.setComponents({ + 'component1': component + }); + + expect(service.getComponentById('component1')).toBe(component); + + service.setComponents(null); + + expect(service.getComponentById('component1')).toBe(component); + }); + + it('should fetch route by id', () => { + const route: RouteRef = { + id: 'test.route', + component: 'component', + path: '/ext/route1' + }; + + spyOn(loader, 'getRoutes').and.returnValue([route]); + service.setup(blankConfig); + + expect(service.getRouteById('test.route')).toBe(route); + }); + + it('should fetch action by id', () => { + const action: ActionRef = { + id: 'test.action', + type: 'action' + }; + + spyOn(loader, 'getActions').and.returnValue([action]); + service.setup(blankConfig); + + expect(service.getActionById('test.action')).toBe(action); + }); + + it('should fetch rule by id', () => { + const rule: RuleRef = { + id: 'test.rule', + type: 'core.every' + }; + + spyOn(loader, 'getRules').and.returnValue([rule]); + service.setup(blankConfig); + + expect(service.getRuleById('test.rule')).toBe(rule); + }); + + it('should evaluate condition', () => { + const condition = () => true; + + service.setEvaluators({ + 'test.condition': condition + }); + + const context: any = { + getEvaluator(key: string) { + return service.getEvaluator(key); + } + }; + + const result = service.evaluateRule('test.condition', context); + expect(result).toBe(true); + }); + + it('should evaluate missing condition as [false]', () => { + const context: any = { + getEvaluator(key: string) { + return service.getEvaluator(key); + } + }; + + const result = service.evaluateRule('missing.condition', context); + expect(result).toBe(false); + }); + + it('should evaluate rule by reference', () => { + const ruleRef: RuleRef = { + id: 'test.rule', + type: 'core.every', + parameters: [ + { + type: 'rule', + value: 'test.condition' + } + ] + }; + + spyOn(loader, 'getRules').and.returnValue([ruleRef]); + service.setup(blankConfig); + + const condition = () => true; + + service.setEvaluators({ + 'test.condition': condition + }); + + const context: any = { + getEvaluator(key: string) { + return service.getEvaluator(key); + } + }; + + const result = service.evaluateRule('test.rule', context); + expect(result).toBe(true); + }); + + it('should evaluate rule ref with missing condition as [false]', () => { + const ruleRef: RuleRef = { + id: 'test.rule', + type: 'missing.evaluator' + }; + + spyOn(loader, 'getRules').and.returnValue([ruleRef]); + service.setup(blankConfig); + + const context: any = { + getEvaluator(key: string) { + return service.getEvaluator(key); + } + }; + + const result = service.evaluateRule('test.rule', context); + expect(result).toBe(false); + }); + + it('should evaluate rule ref with missing evaluator as [false]', () => { + const ruleRef: RuleRef = { + id: 'test.rule', + type: 'core.every', + parameters: [ + { + type: 'rule', + value: 'missing.condition' + } + ] + }; + + spyOn(loader, 'getRules').and.returnValue([ruleRef]); + service.setup(blankConfig); + + const context: any = { + getEvaluator(key: string) { + return service.getEvaluator(key); + } + }; + + const result = service.evaluateRule('test.rule', context); + expect(result).toBe(false); + }); + + describe('expressions', () => { + it('should eval static value', () => { + const value = service.runExpression('hello world'); + expect(value).toBe('hello world'); + }); + + it('should eval string as an expression', () => { + const value = service.runExpression('$( "hello world" )'); + expect(value).toBe('hello world'); + }); + + it('should eval expression with no context', () => { + const value = service.runExpression('$( 1 + 1 )'); + expect(value).toBe(2); + }); + + it('should eval expression with context', () => { + const context = { + a: 'hey', + b: 'there' + }; + const expression = '$( context.a + " " + context.b + "!" )'; + const value = service.runExpression(expression, context); + expect(value).toBe('hey there!'); + }); + }); +}); diff --git a/projects/adf-extensions/src/lib/services/extension.service.ts b/projects/adf-extensions/src/lib/services/extension.service.ts index cf36b5b45..2f78199a3 100644 --- a/projects/adf-extensions/src/lib/services/extension.service.ts +++ b/projects/adf-extensions/src/lib/services/extension.service.ts @@ -49,7 +49,7 @@ export class ExtensionService { setup(config: ExtensionConfig) { if (!config) { - console.error('Extension configuration not found'); + console.warn('Extension configuration not found'); return; } From 06402a9c727bb21436ca0594f232b18ea9b7421f Mon Sep 17 00:00:00 2001 From: Suzana Dirla Date: Thu, 13 Sep 2018 14:32:25 +0300 Subject: [PATCH 012/160] [ACA-1569] wait for dialog to close (#628) --- e2e/suites/application/general.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/suites/application/general.test.ts b/e2e/suites/application/general.test.ts index 3ba41229e..ccced7d09 100644 --- a/e2e/suites/application/general.test.ts +++ b/e2e/suites/application/general.test.ts @@ -76,6 +76,7 @@ describe('General', () => { const message = await page.getSnackBarMessage(); expect(message).toEqual('The action was unsuccessful. Try again or contact your IT Team.'); + await createDialog.waitForDialogToClose(); expect(createDialog.component.isPresent()).not.toBe(true, 'dialog is present'); }); From 883a1971c5582861261f827413c066c838611444 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Thu, 13 Sep 2018 16:47:55 +0100 Subject: [PATCH 013/160] [ACA-1443] prettier formatting and checks (#629) * intergrate prettier * update settings * integrate with travis * unified formatting across all files --- .editorconfig | 2 +- .prettierignore | 2 + .prettierrc | 3 + .travis.yml | 7 +- .vscode/settings.json | 3 +- package-lock.json | 6 + package.json | 4 +- src/app/app.component.ts | 177 +- src/app/app.module.ts | 149 +- src/app/app.routes.strategy.ts | 164 +- src/app/app.routes.ts | 447 +-- .../about/about.component.theme.scss | 58 +- src/app/components/about/about.component.ts | 207 +- src/app/components/about/about.module.ts | 19 +- src/app/components/context-menu/animations.ts | 60 +- .../context-menu-item.directive.ts | 32 +- .../context-menu-outside-event.directive.ts | 38 +- .../context-menu/context-menu-overlay.ts | 9 +- .../context-menu/context-menu.component.scss | 34 +- .../context-menu.component.theme.scss | 24 +- .../context-menu/context-menu.component.ts | 176 +- .../context-menu/context-menu.directive.ts | 184 +- .../context-menu/context-menu.module.ts | 77 +- .../context-menu/context-menu.service.ts | 169 +- src/app/components/context-menu/interfaces.ts | 10 +- .../current-user.component.theme.scss | 78 +- .../current-user/current-user.component.ts | 31 +- .../favorites/favorites.component.spec.ts | 245 +- .../favorites/favorites.component.ts | 133 +- .../components/files/files.component.spec.ts | 496 ++-- src/app/components/files/files.component.ts | 445 +-- .../generic-error.component.theme.scss | 42 +- .../generic-error/generic-error.component.ts | 17 +- .../comments-tab/comments-tab.component.ts | 16 +- .../info-drawer/info-drawer.component.ts | 110 +- .../info-drawer/info.drawer.module.ts | 40 +- .../metadata-tab/metadata-tab.component.ts | 32 +- .../versions-tab/versions-tab.component.ts | 36 +- .../components/layout/layout.component.scss | 16 +- .../layout/layout.component.spec.ts | 147 +- src/app/components/layout/layout.component.ts | 118 +- .../layout/sidenav-views-manager.directive.ts | 151 +- .../libraries/libraries.component.spec.ts | 272 +- .../libraries/libraries.component.ts | 129 +- .../location-link/location-link.component.ts | 243 +- .../components/login/login.component.spec.ts | 85 +- src/app/components/login/login.component.ts | 18 +- src/app/components/page.component.spec.ts | 36 +- src/app/components/page.component.ts | 171 +- .../permissions-manager.component.theme.scss | 84 +- .../permissions-manager.component.ts | 95 +- .../preview/preview-extension.component.ts | 110 +- .../components/preview/preview.component.scss | 4 +- .../preview/preview.component.spec.ts | 1211 ++++---- .../components/preview/preview.component.ts | 607 ++-- src/app/components/preview/preview.module.ts | 41 +- .../components/preview/view-util.service.ts | 375 +-- .../recent-files.component.spec.ts | 154 +- .../recent-files/recent-files.component.ts | 65 +- .../search-input-control.component.scss | 10 +- .../search-input-control.component.ts | 466 +-- .../search-input.component.spec.ts | 109 +- .../search-input.component.theme.scss | 60 +- .../search-input/search-input.component.ts | 209 +- .../search-results-row.component.scss | 16 +- .../search-results-row.component.ts | 131 +- .../search-results.component.scss | 106 +- .../search-results.component.spec.ts | 5 +- .../search-results.component.ts | 244 +- .../settings/settings.component.theme.scss | 118 +- .../components/settings/settings.component.ts | 136 +- .../components/settings/settings.module.ts | 16 +- .../shared-files.component.spec.ts | 143 +- .../shared-files/shared-files.component.ts | 51 +- .../shared-link-view.component.scss | 4 +- .../shared-link-view.component.ts | 28 +- .../components/sidenav/sidenav.component.scss | 88 +- .../sidenav/sidenav.component.spec.ts | 40 +- .../sidenav/sidenav.component.theme.scss | 47 +- .../components/sidenav/sidenav.component.ts | 55 +- .../document-display-mode.component.ts | 35 +- .../toggle-favorite.component.ts | 24 +- .../toggle-info-drawer.component.ts | 18 +- .../toolbar-action.component.ts | 38 +- .../toolbar-button.component.ts | 42 +- src/app/components/toolbar/toolbar.module.ts | 26 +- .../trashcan/trashcan.component.spec.ts | 133 +- .../components/trashcan/trashcan.component.ts | 53 +- src/app/dialogs/library/form.validators.ts | 46 +- src/app/dialogs/library/library.dialog.scss | 17 +- src/app/dialogs/library/library.dialog.ts | 232 +- .../node-permissions.dialog.ts | 16 +- .../node-versions.dialog.theme.scss | 152 +- .../node-versions/node-versions.dialog.ts | 26 +- src/app/directives/directives.module.ts | 16 +- src/app/directives/document-list.directive.ts | 219 +- src/app/directives/experimental.directive.ts | 118 +- src/app/directives/pagination.directive.ts | 52 +- src/app/extensions.module.ts | 20 +- src/app/extensions/core.extensions.module.ts | 132 +- .../extensions/evaluators/app.evaluators.ts | 278 +- .../evaluators/navigation.evaluators.ts | 92 +- src/app/extensions/extension.service.spec.ts | 1042 ++++--- src/app/extensions/extension.service.ts | 388 +-- src/app/material.module.ts | 42 +- src/app/services/content-api.service.ts | 418 ++- .../content-management.service.spec.ts | 2576 +++++++++-------- .../services/content-management.service.ts | 1894 ++++++------ .../services/experimental-guard.service.ts | 48 +- src/app/services/node-actions.service.spec.ts | 2521 +++++++++------- src/app/services/node-actions.service.ts | 1348 +++++---- .../services/node-permission.service.spec.ts | 336 ++- src/app/services/node-permission.service.ts | 86 +- src/app/services/profile.resolver.ts | 46 +- src/app/store/actions/app.actions.ts | 28 +- src/app/store/actions/favorite.actions.ts | 8 +- src/app/store/actions/library.actions.ts | 8 +- src/app/store/actions/node.actions.ts | 56 +- src/app/store/actions/router.actions.ts | 16 +- src/app/store/actions/search.actions.ts | 4 +- src/app/store/actions/snackbar.actions.ts | 34 +- src/app/store/actions/upload.actions.ts | 8 +- src/app/store/actions/viewer.actions.ts | 4 +- src/app/store/app-store.module.ts | 57 +- src/app/store/effects/download.effects.ts | 155 +- src/app/store/effects/favorite.effects.ts | 87 +- src/app/store/effects/library.effects.ts | 64 +- src/app/store/effects/node.effects.ts | 458 ++- src/app/store/effects/router.effects.ts | 174 +- src/app/store/effects/search.effects.ts | 16 +- src/app/store/effects/snackbar.effects.ts | 106 +- src/app/store/effects/upload.effects.ts | 133 +- src/app/store/effects/viewer.effects.ts | 120 +- src/app/store/models/delete-status.model.ts | 18 +- .../store/models/deleted-node-info.model.ts | 6 +- src/app/store/models/node-info.model.ts | 10 +- src/app/store/reducers/app.reducer.ts | 276 +- src/app/store/selectors/app.selectors.ts | 61 +- src/app/store/states/app.state.ts | 74 +- src/app/testing/app-testing.module.ts | 154 +- src/app/testing/translate-pipe.directive.ts | 6 +- src/app/testing/translation.service.ts | 24 +- src/app/ui/application.scss | 21 +- src/app/ui/custom-theme.scss | 110 +- src/app/ui/layout.scss | 88 +- src/app/ui/mixins.scss | 24 +- .../adf-content-node-selector.theme.scss | 26 +- .../ui/overrides/adf-document-list.theme.scss | 200 +- .../ui/overrides/adf-info-drawer.theme.scss | 104 +- .../ui/overrides/adf-layout-header.theme.scss | 58 +- .../ui/overrides/adf-pagination.theme.scss | 32 +- .../ui/overrides/adf-search-filter.theme.scss | 21 +- .../adf-search-sorting-picker.theme.scss | 10 +- .../adf-sidebar-action-menu.theme.scss | 128 +- .../overrides/adf-sidenav-layout.theme.scss | 25 +- src/app/ui/overrides/adf-toolbar.theme.scss | 47 +- .../ui/overrides/adf-upload-button.theme.scss | 98 +- .../ui/overrides/adf-upload-dialog.theme.scss | 48 +- .../overrides/adf-upload-drag-area.theme.scss | 118 +- .../overrides/adf-version-manager.theme.scss | 32 +- src/app/ui/snackbar.scss | 36 +- src/app/ui/theme.scss | 8 +- tslint.json | 82 +- 163 files changed, 13533 insertions(+), 12474 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.editorconfig b/.editorconfig index 4a874ce23..3a5764abc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,7 @@ root = true [*] charset = utf-8 indent_style = space -indent_size = 4 +indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..c3a4b4f13 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +node_modules +src/assets/i18n diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..8db60caac --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/.travis.yml b/.travis.yml index dbb57886b..b60022878 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ addons: language: node_js node_js: - - "8" + - '8' before_script: # Disable services enabled by default @@ -24,7 +24,10 @@ before_install: jobs: include: - stage: test - script: npm run lint && npm run spellcheck + script: + - npm run lint + - npm run spellcheck + - npm run format:check - stage: test script: - npm run test:ci diff --git a/.vscode/settings.json b/.vscode/settings.json index 52ec212c3..00664c444 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "javascript.preferences.quoteStyle": "single", - "typescript.preferences.quoteStyle": "single" + "typescript.preferences.quoteStyle": "single", + "editor.formatOnSave": true } diff --git a/package-lock.json b/package-lock.json index 7fa447ee0..274658697 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11444,6 +11444,12 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "prettier": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", + "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==", + "dev": true + }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", diff --git a/package.json b/package.json index 52bbd2310..20bfdc4e8 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "stop:docker": "docker-compose stop", "e2e:docker": "npm run start:docker && npm run e2e && npm run stop:docker", "spellcheck": "cspell 'src/**/*.ts' 'e2e/**/*.ts' 'projects/**/*.ts'", - "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/app/stats.json" + "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/app/stats.json", + "format:check": "prettier --list-different \"src/{app,environments}/**/*{.ts,.js,.json,.css,.scss}\"" }, "private": true, "dependencies": { @@ -89,6 +90,7 @@ "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", "ng-packagr": "^4.1.1", + "prettier": "^1.14.2", "protractor": "^5.4.0", "rimraf": "2.6.2", "selenium-webdriver": "4.0.0-alpha.1", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 780f0e181..c0c294199 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -24,112 +24,119 @@ */ import { - AlfrescoApiService, - AppConfigService, - AuthenticationService, - FileUploadErrorEvent, - PageTitleService, - UploadService + AlfrescoApiService, + AppConfigService, + AuthenticationService, + FileUploadErrorEvent, + PageTitleService, + UploadService } from '@alfresco/adf-core'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { AppExtensionService } from './extensions/extension.service'; import { - SetLanguagePickerAction, - SnackbarErrorAction, - SetCurrentUrlAction, - SetInitialStateAction + SetLanguagePickerAction, + SnackbarErrorAction, + SetCurrentUrlAction, + SetInitialStateAction } from './store/actions'; -import { AppStore, AppState, INITIAL_APP_STATE } from './store/states/app.state'; +import { + AppStore, + AppState, + INITIAL_APP_STATE +} from './store/states/app.state'; import { filter } from 'rxjs/operators'; import { MatDialog } from '@angular/material'; @Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { - constructor( - private route: ActivatedRoute, - private router: Router, - private pageTitle: PageTitleService, - private store: Store, - private config: AppConfigService, - private alfrescoApiService: AlfrescoApiService, - private authenticationService: AuthenticationService, - private uploadService: UploadService, - private extensions: AppExtensionService, - private dialogRef: MatDialog - ) {} + constructor( + private route: ActivatedRoute, + private router: Router, + private pageTitle: PageTitleService, + private store: Store, + private config: AppConfigService, + private alfrescoApiService: AlfrescoApiService, + private authenticationService: AuthenticationService, + private uploadService: UploadService, + private extensions: AppExtensionService, + private dialogRef: MatDialog + ) {} - ngOnInit() { - this.alfrescoApiService.getInstance().on('error', error => { - if (error.status === 401) { - if (!this.authenticationService.isLoggedIn()) { - this.authenticationService.setRedirect({ provider: 'ECM', url: this.router.url }); - this.router.navigate(['/login']); + ngOnInit() { + this.alfrescoApiService.getInstance().on('error', error => { + if (error.status === 401) { + if (!this.authenticationService.isLoggedIn()) { + this.authenticationService.setRedirect({ + provider: 'ECM', + url: this.router.url + }); + this.router.navigate(['/login']); - this.dialogRef.closeAll(); - } - } - }); + this.dialogRef.closeAll(); + } + } + }); - this.loadAppSettings(); + this.loadAppSettings(); - const { router, pageTitle, route } = this; + const { router, pageTitle, route } = this; - router.events - .pipe(filter(event => event instanceof NavigationEnd)) - .subscribe(() => { - let currentRoute = route.root; + router.events + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe(() => { + let currentRoute = route.root; - while (currentRoute.firstChild) { - currentRoute = currentRoute.firstChild; - } - - const snapshot: any = currentRoute.snapshot || {}; - const data: any = snapshot.data || {}; - - pageTitle.setTitle(data.title || ''); - - this.store.dispatch(new SetCurrentUrlAction(router.url)); - }); - - this.router.config.unshift(...this.extensions.getApplicationRoutes()); - - this.uploadService.fileUploadError.subscribe(error => - this.onFileUploadedError(error) - ); - } - - private loadAppSettings() { - const languagePicker = this.config.get('languagePicker'); - this.store.dispatch(new SetLanguagePickerAction(languagePicker)); - - const state: AppState = { - ... INITIAL_APP_STATE, - appName: this.config.get('application.name'), - headerColor: this.config.get('headerColor'), - logoPath: this.config.get('application.logo'), - sharedUrl: this.config.get('ecmHost') + '/#/preview/s/' - }; - - this.store.dispatch(new SetInitialStateAction(state)); - } - - onFileUploadedError(error: FileUploadErrorEvent) { - let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC'; - - if (error.error.status === 409) { - message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT'; + while (currentRoute.firstChild) { + currentRoute = currentRoute.firstChild; } - if (error.error.status === 500) { - message = 'APP.MESSAGES.UPLOAD.ERROR.500'; - } + const snapshot: any = currentRoute.snapshot || {}; + const data: any = snapshot.data || {}; - this.store.dispatch(new SnackbarErrorAction(message)); + pageTitle.setTitle(data.title || ''); + + this.store.dispatch(new SetCurrentUrlAction(router.url)); + }); + + this.router.config.unshift(...this.extensions.getApplicationRoutes()); + + this.uploadService.fileUploadError.subscribe(error => + this.onFileUploadedError(error) + ); + } + + private loadAppSettings() { + const languagePicker = this.config.get('languagePicker'); + this.store.dispatch(new SetLanguagePickerAction(languagePicker)); + + const state: AppState = { + ...INITIAL_APP_STATE, + appName: this.config.get('application.name'), + headerColor: this.config.get('headerColor'), + logoPath: this.config.get('application.logo'), + sharedUrl: this.config.get('ecmHost') + '/#/preview/s/' + }; + + this.store.dispatch(new SetInitialStateAction(state)); + } + + onFileUploadedError(error: FileUploadErrorEvent) { + let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC'; + + if (error.error.status === 409) { + message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT'; } + + if (error.error.status === 500) { + message = 'APP.MESSAGES.UPLOAD.ERROR.500'; + } + + this.store.dispatch(new SnackbarErrorAction(message)); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9aa1fc6c1..4432d2ad3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -28,7 +28,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, RouteReuseStrategy } from '@angular/router'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { TRANSLATION_PROVIDER, CoreModule, AppConfigService, DebugAppConfigService } from '@alfresco/adf-core'; +import { + TRANSLATION_PROVIDER, + CoreModule, + AppConfigService, + DebugAppConfigService +} from '@alfresco/adf-core'; import { ContentModule } from '@alfresco/adf-content-services'; import { AppComponent } from './app.component'; @@ -75,76 +80,76 @@ import { ExtensionsModule } from '@alfresco/adf-extensions'; import { AppToolbarModule } from './components/toolbar/toolbar.module'; @NgModule({ - imports: [ - BrowserModule, - BrowserAnimationsModule, - FormsModule, - ReactiveFormsModule, - RouterModule.forRoot(APP_ROUTES, { - useHash: true, - enableTracing: false // enable for debug only - }), - MaterialModule, - CoreModule.forRoot(), - ContentModule.forRoot(), - AppStoreModule, - CoreExtensionsModule.forRoot(), - ExtensionsModule.forRoot(), - AppExtensionsModule, + imports: [ + BrowserModule, + BrowserAnimationsModule, + FormsModule, + ReactiveFormsModule, + RouterModule.forRoot(APP_ROUTES, { + useHash: true, + enableTracing: false // enable for debug only + }), + MaterialModule, + CoreModule.forRoot(), + ContentModule.forRoot(), + AppStoreModule, + CoreExtensionsModule.forRoot(), + ExtensionsModule.forRoot(), + AppExtensionsModule, - DirectivesModule, - ContextMenuModule.forRoot(), - AppInfoDrawerModule, - AppToolbarModule - ], - declarations: [ - AppComponent, - GenericErrorComponent, - LoginComponent, - LayoutComponent, - SidenavViewsManagerDirective, - CurrentUserComponent, - SearchInputComponent, - SearchInputControlComponent, - SidenavComponent, - FilesComponent, - FavoritesComponent, - LibrariesComponent, - RecentFilesComponent, - SharedFilesComponent, - TrashcanComponent, - LocationLinkComponent, - SearchResultsRowComponent, - NodeVersionsDialogComponent, - LibraryDialogComponent, - NodePermissionsDialogComponent, - PermissionsManagerComponent, - SearchResultsComponent, - SharedLinkViewComponent - ], - providers: [ - { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, - { provide: AppConfigService, useClass: DebugAppConfigService }, - { - provide: TRANSLATION_PROVIDER, - multi: true, - useValue: { - name: 'app', - source: 'assets' - } - }, - ContentManagementService, - NodeActionsService, - NodePermissionService, - ProfileResolver, - ExperimentalGuard, - ContentApiService - ], - entryComponents: [ - LibraryDialogComponent, - NodeVersionsDialogComponent, - NodePermissionsDialogComponent - ], - bootstrap: [AppComponent] + DirectivesModule, + ContextMenuModule.forRoot(), + AppInfoDrawerModule, + AppToolbarModule + ], + declarations: [ + AppComponent, + GenericErrorComponent, + LoginComponent, + LayoutComponent, + SidenavViewsManagerDirective, + CurrentUserComponent, + SearchInputComponent, + SearchInputControlComponent, + SidenavComponent, + FilesComponent, + FavoritesComponent, + LibrariesComponent, + RecentFilesComponent, + SharedFilesComponent, + TrashcanComponent, + LocationLinkComponent, + SearchResultsRowComponent, + NodeVersionsDialogComponent, + LibraryDialogComponent, + NodePermissionsDialogComponent, + PermissionsManagerComponent, + SearchResultsComponent, + SharedLinkViewComponent + ], + providers: [ + { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, + { provide: AppConfigService, useClass: DebugAppConfigService }, + { + provide: TRANSLATION_PROVIDER, + multi: true, + useValue: { + name: 'app', + source: 'assets' + } + }, + ContentManagementService, + NodeActionsService, + NodePermissionService, + ProfileResolver, + ExperimentalGuard, + ContentApiService + ], + entryComponents: [ + LibraryDialogComponent, + NodeVersionsDialogComponent, + NodePermissionsDialogComponent + ], + bootstrap: [AppComponent] }) -export class AppModule { } +export class AppModule {} diff --git a/src/app/app.routes.strategy.ts b/src/app/app.routes.strategy.ts index 601481aeb..ff5c8e58d 100644 --- a/src/app/app.routes.strategy.ts +++ b/src/app/app.routes.strategy.ts @@ -24,103 +24,101 @@ */ import { - RouteReuseStrategy, - DetachedRouteHandle, - ActivatedRouteSnapshot + RouteReuseStrategy, + DetachedRouteHandle, + ActivatedRouteSnapshot } from '@angular/router'; interface RouteData { - reuse: boolean; + reuse: boolean; } interface RouteInfo { - handle: DetachedRouteHandle; - data: RouteData; + handle: DetachedRouteHandle; + data: RouteData; } export class AppRouteReuseStrategy implements RouteReuseStrategy { - private routeCache = new Map(); + private routeCache = new Map(); - shouldReuseRoute( - future: ActivatedRouteSnapshot, - curr: ActivatedRouteSnapshot - ): boolean { - const ret = future.routeConfig === curr.routeConfig; - if (ret) { - this.addRedirectsRecursively(future); // update redirects + shouldReuseRoute( + future: ActivatedRouteSnapshot, + curr: ActivatedRouteSnapshot + ): boolean { + const ret = future.routeConfig === curr.routeConfig; + if (ret) { + this.addRedirectsRecursively(future); // update redirects + } + return ret; + } + + shouldDetach(route: ActivatedRouteSnapshot): boolean { + const data = this.getRouteData(route); + return data && data.reuse; + } + + store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { + const url = this.getFullRouteUrl(route); + const data = this.getRouteData(route); + this.routeCache.set(url, { handle, data }); + this.addRedirectsRecursively(route); + } + + shouldAttach(route: ActivatedRouteSnapshot): boolean { + const url = this.getFullRouteUrl(route); + return this.routeCache.has(url); + } + + retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { + const url = this.getFullRouteUrl(route); + const data = this.getRouteData(route); + return data && data.reuse && this.routeCache.has(url) + ? this.routeCache.get(url).handle + : null; + } + + private addRedirectsRecursively(route: ActivatedRouteSnapshot): void { + const config = route.routeConfig; + if (config) { + if (!config.loadChildren) { + const routeFirstChild = route.firstChild; + const routeFirstChildUrl = routeFirstChild + ? this.getRouteUrlPaths(routeFirstChild).join('/') + : ''; + const childConfigs = config.children; + if (childConfigs) { + const childConfigWithRedirect = childConfigs.find( + c => c.path === '' && !!c.redirectTo + ); + if (childConfigWithRedirect) { + childConfigWithRedirect.redirectTo = routeFirstChildUrl; + } } - return ret; + } + route.children.forEach(childRoute => + this.addRedirectsRecursively(childRoute) + ); } + } - shouldDetach(route: ActivatedRouteSnapshot): boolean { - const data = this.getRouteData(route); - return data && data.reuse; - } + private getFullRouteUrl(route: ActivatedRouteSnapshot): string { + return this.getFullRouteUrlPaths(route) + .filter(Boolean) + .join('/'); + } - store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { - const url = this.getFullRouteUrl(route); - const data = this.getRouteData(route); - this.routeCache.set(url, { handle, data }); - this.addRedirectsRecursively(route); - } + private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { + const paths = this.getRouteUrlPaths(route); + return route.parent + ? [...this.getFullRouteUrlPaths(route.parent), ...paths] + : paths; + } - shouldAttach(route: ActivatedRouteSnapshot): boolean { - const url = this.getFullRouteUrl(route); - return this.routeCache.has(url); - } + private getRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { + return route.url.map(urlSegment => urlSegment.path); + } - retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { - const url = this.getFullRouteUrl(route); - const data = this.getRouteData(route); - return data && data.reuse && this.routeCache.has(url) - ? this.routeCache.get(url).handle - : null; - } - - private addRedirectsRecursively(route: ActivatedRouteSnapshot): void { - const config = route.routeConfig; - if (config) { - if (!config.loadChildren) { - const routeFirstChild = route.firstChild; - const routeFirstChildUrl = routeFirstChild - ? this.getRouteUrlPaths(routeFirstChild).join('/') - : ''; - const childConfigs = config.children; - if (childConfigs) { - const childConfigWithRedirect = childConfigs.find( - c => c.path === '' && !!c.redirectTo - ); - if (childConfigWithRedirect) { - childConfigWithRedirect.redirectTo = routeFirstChildUrl; - } - } - } - route.children.forEach(childRoute => - this.addRedirectsRecursively(childRoute) - ); - } - } - - private getFullRouteUrl(route: ActivatedRouteSnapshot): string { - return this.getFullRouteUrlPaths(route) - .filter(Boolean) - .join('/'); - } - - private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { - const paths = this.getRouteUrlPaths(route); - return route.parent - ? [...this.getFullRouteUrlPaths(route.parent), ...paths] - : paths; - } - - private getRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { - return route.url.map(urlSegment => urlSegment.path); - } - - private getRouteData(route: ActivatedRouteSnapshot): RouteData { - return ( - route.routeConfig && (route.routeConfig.data as RouteData) - ); - } + private getRouteData(route: ActivatedRouteSnapshot): RouteData { + return route.routeConfig && (route.routeConfig.data as RouteData); + } } diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index f6dea60bd..17e257929 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -43,224 +43,233 @@ import { SearchResultsComponent } from './components/search/search-results/searc import { ProfileResolver } from './services/profile.resolver'; export const APP_ROUTES: Routes = [ - { - path: 'login', - component: LoginComponent, - data: { - title: 'APP.SIGN_IN' - } - }, - { - path: 'settings', - loadChildren: 'src/app/components/settings/settings.module#AppSettingsModule', - data: { - title: 'Settings' - } - }, - { - path: 'preview/s/:id', - component: SharedLinkViewComponent, - data: { - title: 'APP.PREVIEW.TITLE', - } - }, - { - path: '', - component: LayoutComponent, - resolve: { profile: ProfileResolver }, - children: [ - { - path: '', - redirectTo: `/personal-files`, - pathMatch: 'full' - }, - { - path: 'favorites', - data: { - sortingPreferenceKey: 'favorites' - }, - children: [ - { - path: '', - component: FavoritesComponent, - data: { - title: 'APP.BROWSE.FAVORITES.TITLE' - } - }, - { - path: 'preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'favorites' - } - } - ] - }, - { - path: 'libraries', - data: { - sortingPreferenceKey: 'libraries' - }, - children: [{ - path: '', - component: LibrariesComponent, - data: { - title: 'APP.BROWSE.LIBRARIES.TITLE' - } - }, { - path: ':folderId', - component: FilesComponent, - data: { - title: 'APP.BROWSE.LIBRARIES.TITLE', - sortingPreferenceKey: 'libraries-files' - } - }, - { - path: ':folderId/preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'libraries' - } - } - ] - }, - { - path: 'personal-files', - data: { - sortingPreferenceKey: 'personal-files' - }, - children: [ - { - path: '', - component: FilesComponent, - data: { - title: 'APP.BROWSE.PERSONAL.TITLE', - defaultNodeId: '-my-' - } - }, - { - path: ':folderId', - component: FilesComponent, - data: { - title: 'APP.BROWSE.PERSONAL.TITLE' - } - }, - { - path: 'preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'personal-files' - } - }, - { - path: ':folderId/preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'personal-files' - } - } - ] - }, - { - path: 'recent-files', - data: { - sortingPreferenceKey: 'recent-files' - }, - children: [ - { - path: '', - component: RecentFilesComponent, - data: { - title: 'APP.BROWSE.RECENT.TITLE' - } - }, - { - path: 'preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'recent-files' - } - } - ] - }, - { - path: 'shared', - data: { - sortingPreferenceKey: 'shared-files' - }, - children: [ - { - path: '', - component: SharedFilesComponent, - data: { - title: 'APP.BROWSE.SHARED.TITLE' - } - }, - { - path: 'preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'shared' - } - } - ] - }, - { - path: 'trashcan', - component: TrashcanComponent, - data: { - title: 'APP.BROWSE.TRASHCAN.TITLE', - sortingPreferenceKey: 'trashcan' - } - }, - { - path: 'about', - loadChildren: 'src/app/components/about/about.module#AboutModule', - data: { - title: 'APP.BROWSE.ABOUT.TITLE' - } - }, - { - path: 'search', - children: [ - { - path: '', - component: SearchResultsComponent, - data: { - title: 'APP.BROWSE.SEARCH.TITLE', - reuse: true - } - }, - { - path: 'preview/:nodeId', - loadChildren: 'src/app/components/preview/preview.module#PreviewModule', - data: { - title: 'APP.PREVIEW.TITLE', - navigateMultiple: true, - navigateSource: 'search' - } - } - ] - }, - { - path: '**', - component: GenericErrorComponent - } - ], - canActivateChild: [ AuthGuardEcm ], - canActivate: [ AuthGuardEcm ] + { + path: 'login', + component: LoginComponent, + data: { + title: 'APP.SIGN_IN' } + }, + { + path: 'settings', + loadChildren: + 'src/app/components/settings/settings.module#AppSettingsModule', + data: { + title: 'Settings' + } + }, + { + path: 'preview/s/:id', + component: SharedLinkViewComponent, + data: { + title: 'APP.PREVIEW.TITLE' + } + }, + { + path: '', + component: LayoutComponent, + resolve: { profile: ProfileResolver }, + children: [ + { + path: '', + redirectTo: `/personal-files`, + pathMatch: 'full' + }, + { + path: 'favorites', + data: { + sortingPreferenceKey: 'favorites' + }, + children: [ + { + path: '', + component: FavoritesComponent, + data: { + title: 'APP.BROWSE.FAVORITES.TITLE' + } + }, + { + path: 'preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'favorites' + } + } + ] + }, + { + path: 'libraries', + data: { + sortingPreferenceKey: 'libraries' + }, + children: [ + { + path: '', + component: LibrariesComponent, + data: { + title: 'APP.BROWSE.LIBRARIES.TITLE' + } + }, + { + path: ':folderId', + component: FilesComponent, + data: { + title: 'APP.BROWSE.LIBRARIES.TITLE', + sortingPreferenceKey: 'libraries-files' + } + }, + { + path: ':folderId/preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'libraries' + } + } + ] + }, + { + path: 'personal-files', + data: { + sortingPreferenceKey: 'personal-files' + }, + children: [ + { + path: '', + component: FilesComponent, + data: { + title: 'APP.BROWSE.PERSONAL.TITLE', + defaultNodeId: '-my-' + } + }, + { + path: ':folderId', + component: FilesComponent, + data: { + title: 'APP.BROWSE.PERSONAL.TITLE' + } + }, + { + path: 'preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'personal-files' + } + }, + { + path: ':folderId/preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'personal-files' + } + } + ] + }, + { + path: 'recent-files', + data: { + sortingPreferenceKey: 'recent-files' + }, + children: [ + { + path: '', + component: RecentFilesComponent, + data: { + title: 'APP.BROWSE.RECENT.TITLE' + } + }, + { + path: 'preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'recent-files' + } + } + ] + }, + { + path: 'shared', + data: { + sortingPreferenceKey: 'shared-files' + }, + children: [ + { + path: '', + component: SharedFilesComponent, + data: { + title: 'APP.BROWSE.SHARED.TITLE' + } + }, + { + path: 'preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'shared' + } + } + ] + }, + { + path: 'trashcan', + component: TrashcanComponent, + data: { + title: 'APP.BROWSE.TRASHCAN.TITLE', + sortingPreferenceKey: 'trashcan' + } + }, + { + path: 'about', + loadChildren: 'src/app/components/about/about.module#AboutModule', + data: { + title: 'APP.BROWSE.ABOUT.TITLE' + } + }, + { + path: 'search', + children: [ + { + path: '', + component: SearchResultsComponent, + data: { + title: 'APP.BROWSE.SEARCH.TITLE', + reuse: true + } + }, + { + path: 'preview/:nodeId', + loadChildren: + 'src/app/components/preview/preview.module#PreviewModule', + data: { + title: 'APP.PREVIEW.TITLE', + navigateMultiple: true, + navigateSource: 'search' + } + } + ] + }, + { + path: '**', + component: GenericErrorComponent + } + ], + canActivateChild: [AuthGuardEcm], + canActivate: [AuthGuardEcm] + } ]; - diff --git a/src/app/components/about/about.component.theme.scss b/src/app/components/about/about.component.theme.scss index 71be08396..ef30a8e73 100644 --- a/src/app/components/about/about.component.theme.scss +++ b/src/app/components/about/about.component.theme.scss @@ -1,39 +1,39 @@ @mixin aca-about-component-theme($theme) { - $foreground: map-get($theme, foreground); + $foreground: map-get($theme, foreground); - article { - color: mat-color($foreground, text, 0.54); - } + article { + color: mat-color($foreground, text, 0.54); + } - article:first-of-type { - padding-bottom: 0; - } + article:first-of-type { + padding-bottom: 0; + } - article:last-of-type { - margin-bottom: 50px; - } + article:last-of-type { + margin-bottom: 50px; + } - header { - line-height: 24px; - font-size: 14px; - font-weight: 800; - letter-spacing: -0.2px; - } + header { + line-height: 24px; + font-size: 14px; + font-weight: 800; + letter-spacing: -0.2px; + } - a { - text-decoration: none; - color: mat-color($foreground, text, 0.87); - } + a { + text-decoration: none; + color: mat-color($foreground, text, 0.87); + } - .padding { - padding: 25px; - } + .padding { + padding: 25px; + } - .padding-top-bottom { - padding: 25px 0 25px 0; - } + .padding-top-bottom { + padding: 25px 0 25px 0; + } - .padding-left-right { - padding: 0 25px 0 25px; - } + .padding-left-right { + padding: 0 25px 0 25px; + } } diff --git a/src/app/components/about/about.component.ts b/src/app/components/about/about.component.ts index e3d87d088..77972dd3c 100644 --- a/src/app/components/about/about.component.ts +++ b/src/app/components/about/about.component.ts @@ -25,83 +25,164 @@ import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { ObjectDataTableAdapter } from '@alfresco/adf-core'; +import { ObjectDataTableAdapter } from '@alfresco/adf-core'; import { ContentApiService } from '../../services/content-api.service'; import { RepositoryInfo } from 'alfresco-js-api'; import { map } from 'rxjs/operators'; @Component({ - selector: 'app-about', - templateUrl: './about.component.html' + selector: 'app-about', + templateUrl: './about.component.html' }) export class AboutComponent implements OnInit { - repository: RepositoryInfo; - data: ObjectDataTableAdapter; - status: ObjectDataTableAdapter; - license: ObjectDataTableAdapter; - modules: ObjectDataTableAdapter; - githubUrlCommitAlpha = 'https://github.com/Alfresco/alfresco-content-app/commits'; - releaseVersion = ''; + repository: RepositoryInfo; + data: ObjectDataTableAdapter; + status: ObjectDataTableAdapter; + license: ObjectDataTableAdapter; + modules: ObjectDataTableAdapter; + githubUrlCommitAlpha = + 'https://github.com/Alfresco/alfresco-content-app/commits'; + releaseVersion = ''; - constructor( - private contentApi: ContentApiService, - private http: HttpClient - ) {} + constructor( + private contentApi: ContentApiService, + private http: HttpClient + ) {} - ngOnInit() { - this.contentApi.getRepositoryInformation() - .pipe(map(node => node.entry.repository)) - .subscribe(repository => { - this.repository = repository; + ngOnInit() { + this.contentApi + .getRepositoryInformation() + .pipe(map(node => node.entry.repository)) + .subscribe(repository => { + this.repository = repository; - this.modules = new ObjectDataTableAdapter(repository.modules, [ - {type: 'text', key: 'id', title: 'ID', sortable: true}, - {type: 'text', key: 'title', title: 'Title', sortable: true}, - {type: 'text', key: 'version', title: 'Description', sortable: true}, - {type: 'date', key: 'installDate', title: 'Install Date', sortable: true}, - {type: 'text', key: 'installState', title: 'Install State', sortable: true}, - {type: 'text', key: 'versionMin', title: 'Version Minor', sortable: true}, - {type: 'text', key: 'versionMax', title: 'Version Max', sortable: true} - ]); + this.modules = new ObjectDataTableAdapter(repository.modules, [ + { type: 'text', key: 'id', title: 'ID', sortable: true }, + { type: 'text', key: 'title', title: 'Title', sortable: true }, + { + type: 'text', + key: 'version', + title: 'Description', + sortable: true + }, + { + type: 'date', + key: 'installDate', + title: 'Install Date', + sortable: true + }, + { + type: 'text', + key: 'installState', + title: 'Install State', + sortable: true + }, + { + type: 'text', + key: 'versionMin', + title: 'Version Minor', + sortable: true + }, + { + type: 'text', + key: 'versionMax', + title: 'Version Max', + sortable: true + } + ]); - this.status = new ObjectDataTableAdapter([repository.status], [ - {type: 'text', key: 'isReadOnly', title: 'Read Only', sortable: true}, - {type: 'text', key: 'isAuditEnabled', title: 'Audit Enable', sortable: true}, - {type: 'text', key: 'isQuickShareEnabled', title: 'Quick Shared Enable', sortable: true}, - {type: 'text', key: 'isThumbnailGenerationEnabled', title: 'Thumbnail Generation', sortable: true} - ]); - - - if (repository.license) { - this.license = new ObjectDataTableAdapter([repository.license], [ - {type: 'date', key: 'issuedAt', title: 'Issued At', sortable: true}, - {type: 'date', key: 'expiresAt', title: 'Expires At', sortable: true}, - {type: 'text', key: 'remainingDays', title: 'Remaining Days', sortable: true}, - {type: 'text', key: 'holder', title: 'Holder', sortable: true}, - {type: 'text', key: 'mode', title: 'Type', sortable: true}, - {type: 'text', key: 'isClusterEnabled', title: 'Cluster Enabled', sortable: true}, - {type: 'text', key: 'isCryptodocEnabled', title: 'Cryptodoc Enable', sortable: true} - ]); + this.status = new ObjectDataTableAdapter( + [repository.status], + [ + { + type: 'text', + key: 'isReadOnly', + title: 'Read Only', + sortable: true + }, + { + type: 'text', + key: 'isAuditEnabled', + title: 'Audit Enable', + sortable: true + }, + { + type: 'text', + key: 'isQuickShareEnabled', + title: 'Quick Shared Enable', + sortable: true + }, + { + type: 'text', + key: 'isThumbnailGenerationEnabled', + title: 'Thumbnail Generation', + sortable: true } - }); + ] + ); - this.http.get('/versions.json') - .subscribe((response: any) => { - const regexp = new RegExp('^(@alfresco|alfresco-)'); + if (repository.license) { + this.license = new ObjectDataTableAdapter( + [repository.license], + [ + { + type: 'date', + key: 'issuedAt', + title: 'Issued At', + sortable: true + }, + { + type: 'date', + key: 'expiresAt', + title: 'Expires At', + sortable: true + }, + { + type: 'text', + key: 'remainingDays', + title: 'Remaining Days', + sortable: true + }, + { type: 'text', key: 'holder', title: 'Holder', sortable: true }, + { type: 'text', key: 'mode', title: 'Type', sortable: true }, + { + type: 'text', + key: 'isClusterEnabled', + title: 'Cluster Enabled', + sortable: true + }, + { + type: 'text', + key: 'isCryptodocEnabled', + title: 'Cryptodoc Enable', + sortable: true + } + ] + ); + } + }); - const alfrescoPackagesTableRepresentation = Object.keys(response.dependencies) - .filter((val) => regexp.test(val)) - .map((val) => ({ - name: val, - version: response.dependencies[val].version - })); + this.http.get('/versions.json').subscribe((response: any) => { + const regexp = new RegExp('^(@alfresco|alfresco-)'); - this.data = new ObjectDataTableAdapter(alfrescoPackagesTableRepresentation, [ - {type: 'text', key: 'name', title: 'Name', sortable: true}, - {type: 'text', key: 'version', title: 'Version', sortable: true} - ]); + const alfrescoPackagesTableRepresentation = Object.keys( + response.dependencies + ) + .filter(val => regexp.test(val)) + .map(val => ({ + name: val, + version: response.dependencies[val].version + })); - this.releaseVersion = response.version; - }); - } + this.data = new ObjectDataTableAdapter( + alfrescoPackagesTableRepresentation, + [ + { type: 'text', key: 'name', title: 'Name', sortable: true }, + { type: 'text', key: 'version', title: 'Version', sortable: true } + ] + ); + + this.releaseVersion = response.version; + }); + } } diff --git a/src/app/components/about/about.module.ts b/src/app/components/about/about.module.ts index f181e11e4..38ada662f 100644 --- a/src/app/components/about/about.module.ts +++ b/src/app/components/about/about.module.ts @@ -30,19 +30,14 @@ import { CommonModule } from '@angular/common'; import { CoreModule } from '@alfresco/adf-core'; const routes: Routes = [ - { - path: '', - component: AboutComponent - } + { + path: '', + component: AboutComponent + } ]; @NgModule({ - imports: [ - CommonModule, - CoreModule.forChild(), - RouterModule.forChild(routes) - ], - declarations: [AboutComponent] + imports: [CommonModule, CoreModule.forChild(), RouterModule.forChild(routes)], + declarations: [AboutComponent] }) -export class AboutModule { -} +export class AboutModule {} diff --git a/src/app/components/context-menu/animations.ts b/src/app/components/context-menu/animations.ts index 8af081f99..b8d69501f 100644 --- a/src/app/components/context-menu/animations.ts +++ b/src/app/components/context-menu/animations.ts @@ -24,29 +24,45 @@ */ import { - state, - style, - animate, - transition, - query, - group, - sequence + state, + style, + animate, + transition, + query, + group, + sequence } from '@angular/animations'; export const contextMenuAnimation = [ - state('void', style({ - opacity: 0, - transform: 'scale(0.01, 0.01)' - })), - transition('void => *', sequence([ - query('.mat-menu-content', style({ opacity: 0 })), - animate('100ms linear', style({ opacity: 1, transform: 'scale(1, 0.5)' })), - group([ - query('.mat-menu-content', animate('400ms cubic-bezier(0.55, 0, 0.55, 0.2)', - style({ opacity: 1 }) - )), - animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({ transform: 'scale(1, 1)' })), - ]) - ])), - transition('* => void', animate('150ms 50ms linear', style({ opacity: 0 }))) + state( + 'void', + style({ + opacity: 0, + transform: 'scale(0.01, 0.01)' + }) + ), + transition( + 'void => *', + sequence([ + query('.mat-menu-content', style({ opacity: 0 })), + animate( + '100ms linear', + style({ opacity: 1, transform: 'scale(1, 0.5)' }) + ), + group([ + query( + '.mat-menu-content', + animate( + '400ms cubic-bezier(0.55, 0, 0.55, 0.2)', + style({ opacity: 1 }) + ) + ), + animate( + '300ms cubic-bezier(0.25, 0.8, 0.25, 1)', + style({ transform: 'scale(1, 1)' }) + ) + ]) + ]) + ), + transition('* => void', animate('150ms 50ms linear', style({ opacity: 0 }))) ]; diff --git a/src/app/components/context-menu/context-menu-item.directive.ts b/src/app/components/context-menu/context-menu-item.directive.ts index 308a8423a..bc4827505 100644 --- a/src/app/components/context-menu/context-menu-item.directive.ts +++ b/src/app/components/context-menu/context-menu-item.directive.ts @@ -27,25 +27,25 @@ import { Directive, ElementRef, OnDestroy } from '@angular/core'; import { FocusableOption, FocusMonitor, FocusOrigin } from '@angular/cdk/a11y'; @Directive({ - selector: '[acaContextMenuItem]', + selector: '[acaContextMenuItem]' }) export class ContextMenuItemDirective implements OnDestroy, FocusableOption { - constructor( - private elementRef: ElementRef, - private focusMonitor: FocusMonitor) { + constructor( + private elementRef: ElementRef, + private focusMonitor: FocusMonitor + ) { + focusMonitor.monitor(this.getHostElement(), false); + } - focusMonitor.monitor(this.getHostElement(), false); - } + ngOnDestroy() { + this.focusMonitor.stopMonitoring(this.getHostElement()); + } - ngOnDestroy() { - this.focusMonitor.stopMonitoring(this.getHostElement()); - } + focus(origin: FocusOrigin = 'keyboard'): void { + this.focusMonitor.focusVia(this.getHostElement(), origin); + } - focus(origin: FocusOrigin = 'keyboard'): void { - this.focusMonitor.focusVia(this.getHostElement(), origin); - } - - private getHostElement(): HTMLElement { - return this.elementRef.nativeElement; - } + private getHostElement(): HTMLElement { + return this.elementRef.nativeElement; + } } diff --git a/src/app/components/context-menu/context-menu-outside-event.directive.ts b/src/app/components/context-menu/context-menu-outside-event.directive.ts index 89572647b..ff352239b 100644 --- a/src/app/components/context-menu/context-menu-outside-event.directive.ts +++ b/src/app/components/context-menu/context-menu-outside-event.directive.ts @@ -1,28 +1,34 @@ -import { Directive, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; +import { + Directive, + Output, + EventEmitter, + OnInit, + OnDestroy +} from '@angular/core'; import { fromEvent, Subscription } from 'rxjs'; import { delay } from 'rxjs/operators'; @Directive({ selector: '[acaContextMenuOutsideEvent]' }) - export class OutsideEventDirective implements OnInit, OnDestroy { - private subscriptions: Subscription[] = []; + private subscriptions: Subscription[] = []; - @Output() clickOutside: EventEmitter = new EventEmitter(); + @Output() + clickOutside: EventEmitter = new EventEmitter(); - constructor() {} + constructor() {} - ngOnInit() { - this.subscriptions = this.subscriptions.concat([ - fromEvent(document.body, 'click') - .pipe(delay(1)) - .subscribe(() => this.clickOutside.next()) - ]); - } + ngOnInit() { + this.subscriptions = this.subscriptions.concat([ + fromEvent(document.body, 'click') + .pipe(delay(1)) + .subscribe(() => this.clickOutside.next()) + ]); + } - ngOnDestroy() { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); - this.subscriptions = []; - } + ngOnDestroy() { + this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions = []; + } } diff --git a/src/app/components/context-menu/context-menu-overlay.ts b/src/app/components/context-menu/context-menu-overlay.ts index 368138067..9053444b2 100644 --- a/src/app/components/context-menu/context-menu-overlay.ts +++ b/src/app/components/context-menu/context-menu-overlay.ts @@ -26,10 +26,9 @@ import { OverlayRef } from '@angular/cdk/overlay'; export class ContextMenuOverlayRef { + constructor(private overlayRef: OverlayRef) {} - constructor(private overlayRef: OverlayRef) { } - - close(): void { - this.overlayRef.dispose(); - } + close(): void { + this.overlayRef.dispose(); + } } diff --git a/src/app/components/context-menu/context-menu.component.scss b/src/app/components/context-menu/context-menu.component.scss index 21289723e..b55cc8522 100644 --- a/src/app/components/context-menu/context-menu.component.scss +++ b/src/app/components/context-menu/context-menu.component.scss @@ -1,19 +1,19 @@ .aca-context-menu { - &__more-actions::after { - margin-left: 34px; - width: 0; - height: 0; - border-style: solid; - border-width: 5px 0 5px 5px; - content: ''; - display: inline-block; - } + &__more-actions::after { + margin-left: 34px; + width: 0; + height: 0; + border-style: solid; + border-width: 5px 0 5px 5px; + content: ''; + display: inline-block; + } - &__separator { - display: block; - margin: 0; - padding: 0; - border-top-width: 1px; - border-top-style: solid; - } -} \ No newline at end of file + &__separator { + display: block; + margin: 0; + padding: 0; + border-top-width: 1px; + border-top-style: solid; + } +} diff --git a/src/app/components/context-menu/context-menu.component.theme.scss b/src/app/components/context-menu/context-menu.component.theme.scss index f053595a3..4332e7912 100644 --- a/src/app/components/context-menu/context-menu.component.theme.scss +++ b/src/app/components/context-menu/context-menu.component.theme.scss @@ -1,17 +1,17 @@ @mixin aca-context-menu-theme($theme) { - $foreground: map-get($theme, foreground); - $primary: map-get($theme, primary); + $foreground: map-get($theme, foreground); + $primary: map-get($theme, primary); - .aca-context-menu { - @include angular-material-theme($theme); + .aca-context-menu { + @include angular-material-theme($theme); - &__separator { - border-top-color: mat-color($foreground, divider); - } - - &__more-actions::after { - border-color: transparent; - border-left-color: mat-color($primary); - } + &__separator { + border-top-color: mat-color($foreground, divider); } + + &__more-actions::after { + border-color: transparent; + border-left-color: mat-color($primary); + } + } } diff --git a/src/app/components/context-menu/context-menu.component.ts b/src/app/components/context-menu/context-menu.component.ts index 0693d71f2..646817cdb 100644 --- a/src/app/components/context-menu/context-menu.component.ts +++ b/src/app/components/context-menu/context-menu.component.ts @@ -24,8 +24,14 @@ */ import { - Component, ViewEncapsulation, OnInit, OnDestroy, HostListener, - ViewChildren, QueryList, AfterViewInit + Component, + ViewEncapsulation, + OnInit, + OnDestroy, + HostListener, + ViewChildren, + QueryList, + AfterViewInit } from '@angular/core'; import { trigger } from '@angular/animations'; import { FocusKeyManager } from '@angular/cdk/a11y'; @@ -44,99 +50,99 @@ import { contextMenuAnimation } from './animations'; import { ContextMenuItemDirective } from './context-menu-item.directive'; @Component({ - selector: 'aca-context-menu', - templateUrl: './context-menu.component.html', - styleUrls: [ - './context-menu.component.scss', - './context-menu.component.theme.scss' - ], - host: { - role: 'menu', - class: 'aca-context-menu' - }, - encapsulation: ViewEncapsulation.None, - animations: [ - trigger('panelAnimation', contextMenuAnimation) - ] + selector: 'aca-context-menu', + templateUrl: './context-menu.component.html', + styleUrls: [ + './context-menu.component.scss', + './context-menu.component.theme.scss' + ], + host: { + role: 'menu', + class: 'aca-context-menu' + }, + encapsulation: ViewEncapsulation.None, + animations: [trigger('panelAnimation', contextMenuAnimation)] }) export class ContextMenuComponent implements OnInit, OnDestroy, AfterViewInit { - private onDestroy$: Subject = new Subject(); - private selection: SelectionState; - private _keyManager: FocusKeyManager; - actions: Array = []; + private onDestroy$: Subject = new Subject(); + private selection: SelectionState; + private _keyManager: FocusKeyManager; + actions: Array = []; - @ViewChildren(ContextMenuItemDirective) - private contextMenuItems: QueryList; + @ViewChildren(ContextMenuItemDirective) + private contextMenuItems: QueryList; - @HostListener('contextmenu', ['$event']) - handleContextMenu(event: MouseEvent) { - if (event) { - event.preventDefault(); - if (this.contextMenuOverlayRef) { - this.contextMenuOverlayRef.close(); - } - } - } - - @HostListener('document:keydown.Escape', ['$event']) - handleKeydownEscape(event: KeyboardEvent) { - if (event) { - if (this.contextMenuOverlayRef) { - this.contextMenuOverlayRef.close(); - } - } - } - - @HostListener('document:keydown', ['$event']) - handleKeydownEvent(event: KeyboardEvent) { - if (event) { - const keyCode = event.keyCode; - if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) { - this._keyManager.onKeydown(event); - } - } - } - - constructor( - private contextMenuOverlayRef: ContextMenuOverlayRef, - private extensions: AppExtensionService, - private store: Store, - ) { } - - onClickOutsideEvent() { - if (this.contextMenuOverlayRef) { - this.contextMenuOverlayRef.close(); - } - } - - runAction(actionId: string) { - const context = { - selection: this.selection - }; - - this.extensions.runActionById(actionId, context); + @HostListener('contextmenu', ['$event']) + handleContextMenu(event: MouseEvent) { + if (event) { + event.preventDefault(); + if (this.contextMenuOverlayRef) { this.contextMenuOverlayRef.close(); + } } + } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + @HostListener('document:keydown.Escape', ['$event']) + handleKeydownEscape(event: KeyboardEvent) { + if (event) { + if (this.contextMenuOverlayRef) { + this.contextMenuOverlayRef.close(); + } } + } - ngOnInit() { - this.store - .select(appSelection) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(selection => { - if (selection.count) { - this.selection = selection; - this.actions = this.extensions.getAllowedContextMenuActions(); - } - }); + @HostListener('document:keydown', ['$event']) + handleKeydownEvent(event: KeyboardEvent) { + if (event) { + const keyCode = event.keyCode; + if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) { + this._keyManager.onKeydown(event); + } } + } - ngAfterViewInit() { - this._keyManager = new FocusKeyManager(this.contextMenuItems); - this._keyManager.setFirstItemActive(); + constructor( + private contextMenuOverlayRef: ContextMenuOverlayRef, + private extensions: AppExtensionService, + private store: Store + ) {} + + onClickOutsideEvent() { + if (this.contextMenuOverlayRef) { + this.contextMenuOverlayRef.close(); } + } + + runAction(actionId: string) { + const context = { + selection: this.selection + }; + + this.extensions.runActionById(actionId, context); + this.contextMenuOverlayRef.close(); + } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + ngOnInit() { + this.store + .select(appSelection) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(selection => { + if (selection.count) { + this.selection = selection; + this.actions = this.extensions.getAllowedContextMenuActions(); + } + }); + } + + ngAfterViewInit() { + this._keyManager = new FocusKeyManager( + this.contextMenuItems + ); + this._keyManager.setFirstItemActive(); + } } diff --git a/src/app/components/context-menu/context-menu.directive.ts b/src/app/components/context-menu/context-menu.directive.ts index ee489952b..0889fbfc1 100644 --- a/src/app/components/context-menu/context-menu.directive.ts +++ b/src/app/components/context-menu/context-menu.directive.ts @@ -34,106 +34,112 @@ import { DataRow } from '@alfresco/adf-core'; import { MinimalNodeEntity } from 'alfresco-js-api'; @Directive({ - selector: '[acaContextActions]' + selector: '[acaContextActions]' }) export class ContextActionsDirective { - private overlayRef: ContextMenuOverlayRef = null; + private overlayRef: ContextMenuOverlayRef = null; - // tslint:disable-next-line:no-input-rename - @Input('acaContextEnable') enabled = true; + // tslint:disable-next-line:no-input-rename + @Input('acaContextEnable') + enabled = true; - @HostListener('window:resize', ['$event']) - onResize(event) { - if (event && this.overlayRef) { - this.clearSelection(); - this.overlayRef.close(); - } + @HostListener('window:resize', ['$event']) + onResize(event) { + if (event && this.overlayRef) { + this.clearSelection(); + this.overlayRef.close(); + } + } + + @HostListener('contextmenu', ['$event']) + onContextMenuEvent(event: MouseEvent) { + if (event) { + event.preventDefault(); + + if (this.enabled) { + this.execute(event); + } + } + } + + constructor( + private documentList: DocumentListComponent, + private store: Store, + private contextMenuService: ContextMenuService + ) {} + + private execute(event: MouseEvent) { + const selected = this.getSelectedRow(event); + + if (selected) { + if (!this.isInSelection(selected)) { + this.clearSelection(); + + this.documentList.dataTable.selectRow(selected, true); + this.documentList.selection.push((selected).node); + + this.updateSelection(); + } + + this.render(event); + } + } + + private render(event: MouseEvent) { + if (this.overlayRef) { + this.overlayRef.close(); } - @HostListener('contextmenu', ['$event']) - onContextMenuEvent(event: MouseEvent) { - if (event) { - event.preventDefault(); + this.overlayRef = this.contextMenuService.open({ + source: event, + hasBackdrop: false, + backdropClass: 'cdk-overlay-transparent-backdrop', + panelClass: 'cdk-overlay-pane' + }); + } - if (this.enabled) { - this.execute(event); - } - } + private updateSelection() { + this.store.dispatch( + new SetSelectedNodesAction(this.documentList.selection) + ); + } + + private isInSelection(row: DataRow): MinimalNodeEntity { + return this.documentList.selection.find( + selected => row.getValue('name') === selected.entry.name + ); + } + + private getSelectedRow(event): DataRow { + const rowElement = this.findAncestor( + event.target, + 'adf-datatable-row' + ); + + if (!rowElement) { + return null; } - constructor( - private documentList: DocumentListComponent, - private store: Store, - private contextMenuService: ContextMenuService - ) { } + const rowName = rowElement + .querySelector('.adf-data-table-cell--text .adf-datatable-cell') + .textContent.trim(); - private execute(event: MouseEvent) { - const selected = this.getSelectedRow(event); + return this.documentList.data + .getRows() + .find((row: DataRow) => row.getValue('name') === rowName); + } - if (selected) { - if (!this.isInSelection(selected)) { - this.clearSelection(); + private clearSelection() { + this.documentList.data.getRows().map((row: DataRow) => { + return this.documentList.dataTable.selectRow(row, false); + }); - this.documentList.dataTable.selectRow(selected, true); - this.documentList.selection.push((selected).node); + this.documentList.selection = []; + } - this.updateSelection(); - } - - this.render(event); - } - } - - private render(event: MouseEvent) { - if (this.overlayRef) { - this.overlayRef.close(); - } - - this.overlayRef = this.contextMenuService.open({ - source: event, - hasBackdrop: false, - backdropClass: 'cdk-overlay-transparent-backdrop', - panelClass: 'cdk-overlay-pane', - }); - } - - private updateSelection() { - this.store.dispatch( - new SetSelectedNodesAction(this.documentList.selection) - ); - } - - private isInSelection(row: DataRow): MinimalNodeEntity { - return this.documentList.selection.find((selected) => - row.getValue('name') === selected.entry.name); - } - - private getSelectedRow(event): DataRow { - const rowElement = this.findAncestor(event.target, 'adf-datatable-row'); - - if (!rowElement) { - return null; - } - - const rowName = rowElement.querySelector('.adf-data-table-cell--text .adf-datatable-cell') - .textContent - .trim(); - - return this.documentList.data.getRows() - .find((row: DataRow) => row.getValue('name') === rowName); - } - - private clearSelection() { - this.documentList.data.getRows().map((row: DataRow) => { - return this.documentList.dataTable.selectRow(row, false); - }); - - this.documentList.selection = []; - } - - private findAncestor (el: Element, className: string): Element { - // tslint:disable-next-line:curly - while ((el = el.parentElement) && !el.classList.contains(className)); - return el; - } + private findAncestor(el: Element, className: string): Element { + // tslint:disable-next-line:curly + while ((el = el.parentElement) && !el.classList.contains(className)); + return el; + } } diff --git a/src/app/components/context-menu/context-menu.module.ts b/src/app/components/context-menu/context-menu.module.ts index 4e358c1d8..91720b90e 100644 --- a/src/app/components/context-menu/context-menu.module.ts +++ b/src/app/components/context-menu/context-menu.module.ts @@ -24,7 +24,12 @@ */ import { NgModule, ModuleWithProviders } from '@angular/core'; -import { MatMenuModule, MatListModule, MatIconModule, MatButtonModule } from '@angular/material'; +import { + MatMenuModule, + MatListModule, + MatIconModule, + MatButtonModule +} from '@angular/material'; import { BrowserModule } from '@angular/platform-browser'; import { CoreModule } from '@alfresco/adf-core'; import { CoreExtensionsModule } from '../../extensions/core.extensions.module'; @@ -37,44 +42,40 @@ import { ExtensionsModule } from '@alfresco/adf-extensions'; import { OutsideEventDirective } from './context-menu-outside-event.directive'; @NgModule({ - imports: [ - MatMenuModule, - MatListModule, - MatIconModule, - MatButtonModule, - BrowserModule, - CoreExtensionsModule.forChild(), - CoreModule.forChild(), - ExtensionsModule.forChild() - ], - declarations: [ - ContextActionsDirective, - ContextMenuComponent, - ContextMenuItemDirective, - OutsideEventDirective - ], - exports: [ - OutsideEventDirective, - ContextActionsDirective, - ContextMenuComponent - ], - entryComponents: [ - ContextMenuComponent - ] + imports: [ + MatMenuModule, + MatListModule, + MatIconModule, + MatButtonModule, + BrowserModule, + CoreExtensionsModule.forChild(), + CoreModule.forChild(), + ExtensionsModule.forChild() + ], + declarations: [ + ContextActionsDirective, + ContextMenuComponent, + ContextMenuItemDirective, + OutsideEventDirective + ], + exports: [ + OutsideEventDirective, + ContextActionsDirective, + ContextMenuComponent + ], + entryComponents: [ContextMenuComponent] }) export class ContextMenuModule { - static forRoot(): ModuleWithProviders { - return { - ngModule: ContextMenuModule, - providers: [ - ContextMenuService - ] - }; - } + static forRoot(): ModuleWithProviders { + return { + ngModule: ContextMenuModule, + providers: [ContextMenuService] + }; + } - static forChild(): ModuleWithProviders { - return { - ngModule: ContextMenuModule - }; - } + static forChild(): ModuleWithProviders { + return { + ngModule: ContextMenuModule + }; + } } diff --git a/src/app/components/context-menu/context-menu.service.ts b/src/app/components/context-menu/context-menu.service.ts index 43b681066..a57d581a8 100644 --- a/src/app/components/context-menu/context-menu.service.ts +++ b/src/app/components/context-menu/context-menu.service.ts @@ -7,96 +7,115 @@ import { ContextmenuOverlayConfig } from './interfaces'; @Injectable() export class ContextMenuService { - constructor( - private injector: Injector, - private overlay: Overlay) { } + constructor(private injector: Injector, private overlay: Overlay) {} - open(config: ContextmenuOverlayConfig) { + open(config: ContextmenuOverlayConfig) { + const overlay = this.createOverlay(config); - const overlay = this.createOverlay(config); + const overlayRef = new ContextMenuOverlayRef(overlay); - const overlayRef = new ContextMenuOverlayRef(overlay); + this.attachDialogContainer(overlay, config, overlayRef); - this.attachDialogContainer(overlay, config, overlayRef); + overlay.backdropClick().subscribe(() => overlayRef.close()); - overlay.backdropClick().subscribe(() => overlayRef.close()); - - // prevent native contextmenu on overlay element if config.hasBackdrop is true - if (config.hasBackdrop) { - (overlay)._backdropElement - .addEventListener('contextmenu', () => { - event.preventDefault(); - (overlay)._backdropClick.next(null); - }, true); - } - - return overlayRef; + // prevent native contextmenu on overlay element if config.hasBackdrop is true + if (config.hasBackdrop) { + (overlay)._backdropElement.addEventListener( + 'contextmenu', + () => { + event.preventDefault(); + (overlay)._backdropClick.next(null); + }, + true + ); } - private createOverlay(config: ContextmenuOverlayConfig) { - const overlayConfig = this.getOverlayConfig(config); - return this.overlay.create(overlayConfig); - } + return overlayRef; + } - private attachDialogContainer(overlay: OverlayRef, config: ContextmenuOverlayConfig, contextmenuOverlayRef: ContextMenuOverlayRef) { - const injector = this.createInjector(config, contextmenuOverlayRef); + private createOverlay(config: ContextmenuOverlayConfig) { + const overlayConfig = this.getOverlayConfig(config); + return this.overlay.create(overlayConfig); + } - const containerPortal = new ComponentPortal(ContextMenuComponent, null, injector); - const containerRef: ComponentRef = overlay.attach(containerPortal); + private attachDialogContainer( + overlay: OverlayRef, + config: ContextmenuOverlayConfig, + contextmenuOverlayRef: ContextMenuOverlayRef + ) { + const injector = this.createInjector(config, contextmenuOverlayRef); - return containerRef.instance; - } + const containerPortal = new ComponentPortal( + ContextMenuComponent, + null, + injector + ); + const containerRef: ComponentRef = overlay.attach( + containerPortal + ); - private createInjector(config: ContextmenuOverlayConfig, contextmenuOverlayRef: ContextMenuOverlayRef): PortalInjector { - const injectionTokens = new WeakMap(); + return containerRef.instance; + } - injectionTokens.set(ContextMenuOverlayRef, contextmenuOverlayRef); + private createInjector( + config: ContextmenuOverlayConfig, + contextmenuOverlayRef: ContextMenuOverlayRef + ): PortalInjector { + const injectionTokens = new WeakMap(); - return new PortalInjector(this.injector, injectionTokens); - } + injectionTokens.set(ContextMenuOverlayRef, contextmenuOverlayRef); - private getOverlayConfig(config: ContextmenuOverlayConfig): OverlayConfig { - const fakeElement: any = { - getBoundingClientRect: (): ClientRect => ({ - bottom: config.source.clientY, - height: 0, - left: config.source.clientX, - right: config.source.clientX, - top: config.source.clientY, - width: 0 - }) - }; + return new PortalInjector(this.injector, injectionTokens); + } - const positionStrategy = this.overlay.position() - .connectedTo( - new ElementRef(fakeElement), - { originX: 'start', originY: 'bottom' }, - { overlayX: 'start', overlayY: 'top' }) - .withFallbackPosition( - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'bottom' }) - .withFallbackPosition( - { originX: 'end', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' }) - .withFallbackPosition( - { originX: 'start', originY: 'top' }, - { overlayX: 'end', overlayY: 'top' }) - .withFallbackPosition( - { originX: 'end', originY: 'center' }, - { overlayX: 'start', overlayY: 'center' }) - .withFallbackPosition( - { originX: 'start', originY: 'center' }, - { overlayX: 'end', overlayY: 'center' } - ); + private getOverlayConfig(config: ContextmenuOverlayConfig): OverlayConfig { + const fakeElement: any = { + getBoundingClientRect: (): ClientRect => ({ + bottom: config.source.clientY, + height: 0, + left: config.source.clientX, + right: config.source.clientX, + top: config.source.clientY, + width: 0 + }) + }; - const overlayConfig = new OverlayConfig({ - hasBackdrop: config.hasBackdrop, - backdropClass: config.backdropClass, - panelClass: config.panelClass, - scrollStrategy: this.overlay.scrollStrategies.close(), - positionStrategy - }); + const positionStrategy = this.overlay + .position() + .connectedTo( + new ElementRef(fakeElement), + { originX: 'start', originY: 'bottom' }, + { overlayX: 'start', overlayY: 'top' } + ) + .withFallbackPosition( + { originX: 'start', originY: 'top' }, + { overlayX: 'start', overlayY: 'bottom' } + ) + .withFallbackPosition( + { originX: 'end', originY: 'top' }, + { overlayX: 'start', overlayY: 'top' } + ) + .withFallbackPosition( + { originX: 'start', originY: 'top' }, + { overlayX: 'end', overlayY: 'top' } + ) + .withFallbackPosition( + { originX: 'end', originY: 'center' }, + { overlayX: 'start', overlayY: 'center' } + ) + .withFallbackPosition( + { originX: 'start', originY: 'center' }, + { overlayX: 'end', overlayY: 'center' } + ); - return overlayConfig; - } + const overlayConfig = new OverlayConfig({ + hasBackdrop: config.hasBackdrop, + backdropClass: config.backdropClass, + panelClass: config.panelClass, + scrollStrategy: this.overlay.scrollStrategies.close(), + positionStrategy + }); + + return overlayConfig; + } } diff --git a/src/app/components/context-menu/interfaces.ts b/src/app/components/context-menu/interfaces.ts index 7fb06f250..51fb28324 100644 --- a/src/app/components/context-menu/interfaces.ts +++ b/src/app/components/context-menu/interfaces.ts @@ -24,9 +24,9 @@ */ export interface ContextmenuOverlayConfig { - panelClass?: string; - hasBackdrop?: boolean; - backdropClass?: string; - source?: MouseEvent; - data?: any; + panelClass?: string; + hasBackdrop?: boolean; + backdropClass?: string; + source?: MouseEvent; + data?: any; } diff --git a/src/app/components/current-user/current-user.component.theme.scss b/src/app/components/current-user/current-user.component.theme.scss index b3ab0dfec..645a9e0ac 100644 --- a/src/app/components/current-user/current-user.component.theme.scss +++ b/src/app/components/current-user/current-user.component.theme.scss @@ -1,44 +1,44 @@ @mixin aca-current-user-theme($theme) { - $background: map-get($theme, background); - $am-avatar-size: 35px; + $background: map-get($theme, background); + $am-avatar-size: 35px; - .aca-current-user { - position: relative; - color: mat-color($background, card); - line-height: 20px; + .aca-current-user { + position: relative; + color: mat-color($background, card); + line-height: 20px; - .am-avatar { - margin-left: 9px; - cursor: pointer; - display: inline-block; - width: $am-avatar-size; - height: $am-avatar-size; - line-height: $am-avatar-size; - font-size: 1em; - text-align: center; - color: inherit; - border-radius: 100%; - background-color: mat-color($background, card, .15); - } - - .current-user__full-name { - width: 100px; - text-align: right; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display:inline-block; - height: 18px; - font-size: 14px; - line-height: 1.43; - letter-spacing: -0.3px; - cursor: default; - } - - @media screen and ($mat-small) { - .current-user__full-name { - display: none; - } - } + .am-avatar { + margin-left: 9px; + cursor: pointer; + display: inline-block; + width: $am-avatar-size; + height: $am-avatar-size; + line-height: $am-avatar-size; + font-size: 1em; + text-align: center; + color: inherit; + border-radius: 100%; + background-color: mat-color($background, card, 0.15); } + + .current-user__full-name { + width: 100px; + text-align: right; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + height: 18px; + font-size: 14px; + line-height: 1.43; + letter-spacing: -0.3px; + cursor: default; + } + + @media screen and ($mat-small) { + .current-user__full-name { + display: none; + } + } + } } diff --git a/src/app/components/current-user/current-user.component.ts b/src/app/components/current-user/current-user.component.ts index efd0aa26b..f250d3e5c 100644 --- a/src/app/components/current-user/current-user.component.ts +++ b/src/app/components/current-user/current-user.component.ts @@ -26,27 +26,30 @@ import { Component, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { selectUser, appLanguagePicker } from '../../store/selectors/app.selectors'; +import { + selectUser, + appLanguagePicker +} from '../../store/selectors/app.selectors'; import { AppStore } from '../../store/states'; import { ProfileState } from '@alfresco/adf-extensions'; import { SetSelectedNodesAction } from '../../store/actions'; @Component({ - selector: 'aca-current-user', - templateUrl: './current-user.component.html', - encapsulation: ViewEncapsulation.None, - host: { class: 'aca-current-user' } + selector: 'aca-current-user', + templateUrl: './current-user.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'aca-current-user' } }) export class CurrentUserComponent { - profile$: Observable; - languagePicker$: Observable; + profile$: Observable; + languagePicker$: Observable; - constructor(private store: Store) { - this.profile$ = this.store.select(selectUser); - this.languagePicker$ = store.select(appLanguagePicker); - } + constructor(private store: Store) { + this.profile$ = this.store.select(selectUser); + this.languagePicker$ = store.select(appLanguagePicker); + } - onLogoutEvent() { - this.store.dispatch(new SetSelectedNodesAction([])); - } + onLogoutEvent() { + this.store.dispatch(new SetSelectedNodesAction([])); + } } diff --git a/src/app/components/favorites/favorites.component.spec.ts b/src/app/components/favorites/favorites.component.spec.ts index 87fd447e1..a2fdfd4ad 100644 --- a/src/app/components/favorites/favorites.component.spec.ts +++ b/src/app/components/favorites/favorites.component.spec.ts @@ -27,9 +27,12 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router } from '@angular/router'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { - AlfrescoApiService, - TimeAgoPipe, NodeNameTooltipPipe, - NodeFavoriteDirective, DataTableComponent, AppConfigPipe + AlfrescoApiService, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DataTableComponent, + AppConfigPipe } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../services/content-management.service'; @@ -40,136 +43,144 @@ import { ContentApiService } from '../../services/content-api.service'; import { ExperimentalDirective } from '../../directives/experimental.directive'; describe('FavoritesComponent', () => { - let fixture: ComponentFixture; - let component: FavoritesComponent; - let alfrescoApi: AlfrescoApiService; - let contentService: ContentManagementService; - let contentApi: ContentApiService; - let router: Router; - let page; - let node; + let fixture: ComponentFixture; + let component: FavoritesComponent; + let alfrescoApi: AlfrescoApiService; + let contentService: ContentManagementService; + let contentApi: ContentApiService; + let router: Router; + let page; + let node; + beforeEach(() => { + page = { + list: { + entries: [ + { entry: { id: 1, target: { file: {} } } }, + { entry: { id: 2, target: { folder: {} } } } + ], + pagination: { data: 'data' } + } + }; + + node = { + id: 'folder-node', + isFolder: true, + isFile: false, + path: { + elements: [] + } + }; + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + DataTableComponent, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DocumentListComponent, + FavoritesComponent, + AppConfigPipe, + ExperimentalDirective + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(FavoritesComponent); + component = fixture.componentInstance; + + alfrescoApi = TestBed.get(AlfrescoApiService); + alfrescoApi.reset(); + spyOn(alfrescoApi.favoritesApi, 'getFavorites').and.returnValue( + Promise.resolve(page) + ); + + contentApi = TestBed.get(ContentApiService); + + contentService = TestBed.get(ContentManagementService); + router = TestBed.get(Router); + }); + + describe('Events', () => { beforeEach(() => { - page = { - list: { - entries: [ - { entry: { id: 1, target: { file: {} } } }, - { entry: { id: 2, target: { folder: {} } } } - ], - pagination: { data: 'data'} - } - }; - - node = { - id: 'folder-node', - isFolder: true, - isFile: false, - path: { - elements: [] - } - }; + spyOn(component, 'reload'); + fixture.detectChanges(); }); + it('should refresh on editing folder event', () => { + contentService.folderEdited.next(null); + + expect(component.reload).toHaveBeenCalled(); + }); + + it('should refresh on move node event', () => { + contentService.nodesMoved.next(null); + + expect(component.reload).toHaveBeenCalled(); + }); + + it('should refresh on node deleted event', () => { + contentService.nodesDeleted.next(null); + + expect(component.reload).toHaveBeenCalled(); + }); + + it('should refresh on node restore event', () => { + contentService.nodesRestored.next(null); + + expect(component.reload).toHaveBeenCalled(); + }); + }); + + describe('Node navigation', () => { beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ AppTestingModule ], - declarations: [ - DataTableComponent, - TimeAgoPipe, - NodeNameTooltipPipe, - NodeFavoriteDirective, - DocumentListComponent, - FavoritesComponent, - AppConfigPipe, - ExperimentalDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); - - fixture = TestBed.createComponent(FavoritesComponent); - component = fixture.componentInstance; - - alfrescoApi = TestBed.get(AlfrescoApiService); - alfrescoApi.reset(); - spyOn(alfrescoApi.favoritesApi, 'getFavorites').and.returnValue(Promise.resolve(page)); - - contentApi = TestBed.get(ContentApiService); - - contentService = TestBed.get(ContentManagementService); - router = TestBed.get(Router); + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + spyOn(router, 'navigate'); + fixture.detectChanges(); }); - describe('Events', () => { - beforeEach(() => { - spyOn(component, 'reload'); - fixture.detectChanges(); - }); + it('navigates to `/libraries` if node path has `Sites`', () => { + node.path.elements = [{ name: 'Sites' }]; - it('should refresh on editing folder event', () => { - contentService.folderEdited.next(null); + component.navigate(node); - expect(component.reload).toHaveBeenCalled(); - }); - - it('should refresh on move node event', () => { - contentService.nodesMoved.next(null); - - expect(component.reload).toHaveBeenCalled(); - }); - - it('should refresh on node deleted event', () => { - contentService.nodesDeleted.next(null); - - expect(component.reload).toHaveBeenCalled(); - }); - - it('should refresh on node restore event', () => { - contentService.nodesRestored.next(null); - - expect(component.reload).toHaveBeenCalled(); - }); + expect(router.navigate).toHaveBeenCalledWith([ + '/libraries', + 'folder-node' + ]); }); - describe('Node navigation', () => { - beforeEach(() => { - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node})); - spyOn(router, 'navigate'); - fixture.detectChanges(); - }); + it('navigates to `/personal-files` if node path has no `Sites`', () => { + node.path.elements = [{ name: 'something else' }]; - it('navigates to `/libraries` if node path has `Sites`', () => { - node.path.elements = [{ name: 'Sites' }]; + component.navigate(node); - component.navigate(node); - - expect(router.navigate).toHaveBeenCalledWith([ '/libraries', 'folder-node' ]); - }); - - it('navigates to `/personal-files` if node path has no `Sites`', () => { - node.path.elements = [{ name: 'something else' }]; - - component.navigate(node); - - expect(router.navigate).toHaveBeenCalledWith([ '/personal-files', 'folder-node' ]); - }); - - it('does not navigate when node is not folder', () => { - node.isFolder = false; - - component.navigate(node); - - expect(router.navigate).not.toHaveBeenCalled(); - }); + expect(router.navigate).toHaveBeenCalledWith([ + '/personal-files', + 'folder-node' + ]); }); - describe('refresh', () => { - it('should call document list reload', () => { - spyOn(component.documentList, 'reload'); - fixture.detectChanges(); + it('does not navigate when node is not folder', () => { + node.isFolder = false; - component.reload(); + component.navigate(node); - expect(component.documentList.reload).toHaveBeenCalled(); - }); + expect(router.navigate).not.toHaveBeenCalled(); }); + }); + + describe('refresh', () => { + it('should call document list reload', () => { + spyOn(component.documentList, 'reload'); + fixture.detectChanges(); + + component.reload(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/components/favorites/favorites.component.ts b/src/app/components/favorites/favorites.component.ts index 54b71ecaa..589befa2a 100644 --- a/src/app/components/favorites/favorites.component.ts +++ b/src/app/components/favorites/favorites.component.ts @@ -28,10 +28,10 @@ import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { - MinimalNodeEntity, - MinimalNodeEntryEntity, - PathElementEntity, - PathInfo + MinimalNodeEntity, + MinimalNodeEntryEntity, + PathElementEntity, + PathInfo } from 'alfresco-js-api'; import { ContentManagementService } from '../../services/content-management.service'; import { AppStore } from '../../store/states/app.state'; @@ -41,76 +41,71 @@ import { AppExtensionService } from '../../extensions/extension.service'; import { map } from 'rxjs/operators'; @Component({ - templateUrl: './favorites.component.html' + templateUrl: './favorites.component.html' }) export class FavoritesComponent extends PageComponent implements OnInit { - isSmallScreen = false; + isSmallScreen = false; - constructor( - private router: Router, - store: Store, - extensions: AppExtensionService, - private contentApi: ContentApiService, - content: ContentManagementService, - private breakpointObserver: BreakpointObserver - ) { - super(store, extensions, content); + constructor( + private router: Router, + store: Store, + extensions: AppExtensionService, + private contentApi: ContentApiService, + content: ContentManagementService, + private breakpointObserver: BreakpointObserver + ) { + super(store, extensions, content); + } + + ngOnInit() { + super.ngOnInit(); + + this.subscriptions = this.subscriptions.concat([ + this.content.nodesDeleted.subscribe(() => this.reload()), + this.content.nodesRestored.subscribe(() => this.reload()), + this.content.folderEdited.subscribe(() => this.reload()), + this.content.nodesMoved.subscribe(() => this.reload()), + this.content.favoriteRemoved.subscribe(() => this.reload()), + this.content.favoriteToggle.subscribe(() => this.reload()), + + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ]); + } + + navigate(favorite: MinimalNodeEntryEntity) { + const { isFolder, id } = favorite; + + // TODO: rework as it will fail on non-English setups + const isSitePath = (path: PathInfo): boolean => { + return path.elements.some( + ({ name }: PathElementEntity) => name === 'Sites' + ); + }; + + if (isFolder) { + this.contentApi + .getNode(id) + .pipe(map(node => node.entry)) + .subscribe(({ path }: MinimalNodeEntryEntity) => { + const routeUrl = isSitePath(path) ? '/libraries' : '/personal-files'; + this.router.navigate([routeUrl, id]); + }); } + } - ngOnInit() { - super.ngOnInit(); + onNodeDoubleClick(node: MinimalNodeEntity) { + if (node && node.entry) { + if (node.entry.isFolder) { + this.navigate(node.entry); + } - this.subscriptions = this.subscriptions.concat([ - this.content.nodesDeleted.subscribe(() => this.reload()), - this.content.nodesRestored.subscribe(() => this.reload()), - this.content.folderEdited.subscribe(() => this.reload()), - this.content.nodesMoved.subscribe(() => this.reload()), - this.content.favoriteRemoved.subscribe(() => this.reload()), - this.content.favoriteToggle.subscribe(() => this.reload()), - - this.breakpointObserver - .observe([ - Breakpoints.HandsetPortrait, - Breakpoints.HandsetLandscape - ]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }) - ]); - } - - navigate(favorite: MinimalNodeEntryEntity) { - const { isFolder, id } = favorite; - - // TODO: rework as it will fail on non-English setups - const isSitePath = (path: PathInfo): boolean => { - return path.elements.some( - ({ name }: PathElementEntity) => name === 'Sites' - ); - }; - - if (isFolder) { - this.contentApi - .getNode(id) - .pipe(map(node => node.entry)) - .subscribe(({ path }: MinimalNodeEntryEntity) => { - const routeUrl = isSitePath(path) - ? '/libraries' - : '/personal-files'; - this.router.navigate([routeUrl, id]); - }); - } - } - - onNodeDoubleClick(node: MinimalNodeEntity) { - if (node && node.entry) { - if (node.entry.isFolder) { - this.navigate(node.entry); - } - - if (node.entry.isFile) { - this.showPreview(node); - } - } + if (node.entry.isFile) { + this.showPreview(node); + } } + } } diff --git a/src/app/components/files/files.component.spec.ts b/src/app/components/files/files.component.spec.ts index e6ebc4252..a5d3eae35 100644 --- a/src/app/components/files/files.component.spec.ts +++ b/src/app/components/files/files.component.spec.ts @@ -23,14 +23,22 @@ * along with Alfresco. If not, see . */ -import { TestBed, fakeAsync, tick, ComponentFixture } from '@angular/core/testing'; +import { + TestBed, + fakeAsync, + tick, + ComponentFixture +} from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { - TimeAgoPipe, NodeNameTooltipPipe, FileSizePipe, NodeFavoriteDirective, - DataTableComponent, - UploadService, - AppConfigPipe + TimeAgoPipe, + NodeNameTooltipPipe, + FileSizePipe, + NodeFavoriteDirective, + DataTableComponent, + UploadService, + AppConfigPipe } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../services/content-management.service'; @@ -42,270 +50,278 @@ import { ExperimentalDirective } from '../../directives/experimental.directive'; import { of, throwError } from 'rxjs'; describe('FilesComponent', () => { - let node; - let fixture: ComponentFixture; - let component: FilesComponent; - let contentManagementService: ContentManagementService; - let uploadService: UploadService; - let router: Router; - let nodeActionsService: NodeActionsService; - let contentApi: ContentApiService; + let node; + let fixture: ComponentFixture; + let component: FilesComponent; + let contentManagementService: ContentManagementService; + let uploadService: UploadService; + let router: Router; + let nodeActionsService: NodeActionsService; + let contentApi: ContentApiService; + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + FilesComponent, + DataTableComponent, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DocumentListComponent, + FileSizePipe, + AppConfigPipe, + ExperimentalDirective + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { data: { preferencePrefix: 'prefix' } }, + params: of({ folderId: 'someId' }) + } + } + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(FilesComponent); + component = fixture.componentInstance; + + contentManagementService = TestBed.get(ContentManagementService); + uploadService = TestBed.get(UploadService); + router = TestBed.get(Router); + nodeActionsService = TestBed.get(NodeActionsService); + contentApi = TestBed.get(ContentApiService); + }); + + beforeEach(() => { + node = { id: 'node-id', isFolder: true }; + spyOn(component.documentList, 'loadFolder').and.callFake(() => {}); + }); + + describe('Current page is valid', () => { + it('should be a valid current page', fakeAsync(() => { + spyOn(contentApi, 'getNode').and.returnValue(throwError(null)); + + component.ngOnInit(); + fixture.detectChanges(); + tick(); + + expect(component.isValidPath).toBe(false); + })); + + it('should set current page as invalid path', fakeAsync(() => { + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + + component.ngOnInit(); + tick(); + fixture.detectChanges(); + + expect(component.isValidPath).toBe(true); + })); + }); + + describe('OnInit', () => { + it('should set current node', () => { + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + fixture.detectChanges(); + expect(component.node).toBe(node); + }); + + it('if should navigate to parent if node is not a folder', () => { + node.isFolder = false; + node.parentId = 'parent-id'; + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + spyOn(router, 'navigate'); + + fixture.detectChanges(); + + expect(router.navigate['calls'].argsFor(0)[0]).toEqual([ + '/personal-files', + 'parent-id' + ]); + }); + }); + + describe('refresh on events', () => { beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ AppTestingModule ], - declarations: [ - FilesComponent, - DataTableComponent, - TimeAgoPipe, - NodeNameTooltipPipe, - NodeFavoriteDirective, - DocumentListComponent, - FileSizePipe, - AppConfigPipe, - ExperimentalDirective - ], - providers: [ - { provide: ActivatedRoute, useValue: { - snapshot: { data: { preferencePrefix: 'prefix' } }, - params: of({ folderId: 'someId' }) - } } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + spyOn(component.documentList, 'reload'); - fixture = TestBed.createComponent(FilesComponent); - component = fixture.componentInstance; - - contentManagementService = TestBed.get(ContentManagementService); - uploadService = TestBed.get(UploadService); - router = TestBed.get(Router); - nodeActionsService = TestBed.get(NodeActionsService); - contentApi = TestBed.get(ContentApiService); + fixture.detectChanges(); }); + it('should call refresh onContentCopied event if parent is the same', () => { + const nodes = [ + { entry: { parentId: '1' } }, + { entry: { parentId: '2' } } + ]; + + component.node = { id: '1' }; + + nodeActionsService.contentCopied.next(nodes); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + + it('should not call refresh onContentCopied event when parent mismatch', () => { + const nodes = [ + { entry: { parentId: '1' } }, + { entry: { parentId: '2' } } + ]; + + component.node = { id: '3' }; + + nodeActionsService.contentCopied.next(nodes); + + expect(component.documentList.reload).not.toHaveBeenCalled(); + }); + + it('should call refresh onCreateFolder event', () => { + contentManagementService.folderCreated.next(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + + it('should call refresh editFolder event', () => { + contentManagementService.folderEdited.next(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + + it('should call refresh deleteNode event', () => { + contentManagementService.nodesDeleted.next(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + + it('should call refresh moveNode event', () => { + contentManagementService.nodesMoved.next(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + + it('should call refresh restoreNode event', () => { + contentManagementService.nodesRestored.next(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + + it('should call refresh on fileUploadComplete event if parent node match', fakeAsync(() => { + const file = { file: { options: { parentId: 'parentId' } } }; + component.node = { id: 'parentId' }; + + uploadService.fileUploadComplete.next(file); + + tick(500); + + expect(component.documentList.reload).toHaveBeenCalled(); + })); + + it('should not call refresh on fileUploadComplete event if parent mismatch', fakeAsync(() => { + const file = { file: { options: { parentId: 'otherId' } } }; + component.node = { id: 'parentId' }; + + uploadService.fileUploadComplete.next(file); + + tick(500); + + expect(component.documentList.reload).not.toHaveBeenCalled(); + })); + + it('should call refresh on fileUploadDeleted event if parent node match', fakeAsync(() => { + const file = { file: { options: { parentId: 'parentId' } } }; + component.node = { id: 'parentId' }; + + uploadService.fileUploadDeleted.next(file); + + tick(500); + + expect(component.documentList.reload).toHaveBeenCalled(); + })); + + it('should not call refresh on fileUploadDeleted event if parent mismatch', fakeAsync(() => { + const file: any = { file: { options: { parentId: 'otherId' } } }; + component.node = { id: 'parentId' }; + + uploadService.fileUploadDeleted.next(file); + + tick(500); + + expect(component.documentList.reload).not.toHaveBeenCalled(); + })); + }); + + describe('onBreadcrumbNavigate()', () => { beforeEach(() => { - node = { id: 'node-id', isFolder: true }; - spyOn(component.documentList, 'loadFolder').and.callFake(() => {}); + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + fixture.detectChanges(); }); - describe('Current page is valid', () => { - it('should be a valid current page', fakeAsync(() => { - spyOn(contentApi, 'getNode').and.returnValue(throwError(null)); + it('should navigates to node id', () => { + const routeData: any = { id: 'some-where-over-the-rainbow' }; + spyOn(component, 'navigate'); - component.ngOnInit(); - fixture.detectChanges(); - tick(); + component.onBreadcrumbNavigate(routeData); - expect(component.isValidPath).toBe(false); - })); + expect(component.navigate).toHaveBeenCalledWith(routeData.id); + }); + }); - it('should set current page as invalid path', fakeAsync(() => { - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + describe('Node navigation', () => { + beforeEach(() => { + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); + spyOn(router, 'navigate'); - component.ngOnInit(); - tick(); - fixture.detectChanges(); - - expect(component.isValidPath).toBe(true); - })); + fixture.detectChanges(); }); - describe('OnInit', () => { - it('should set current node', () => { - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); - fixture.detectChanges(); - expect(component.node).toBe(node); - }); + it('should navigates to node when id provided', () => { + component.navigate(node.id); - it('if should navigate to parent if node is not a folder', () => { - node.isFolder = false; - node.parentId = 'parent-id'; - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); - spyOn(router, 'navigate'); - - fixture.detectChanges(); - - expect(router.navigate['calls'].argsFor(0)[0]).toEqual(['/personal-files', 'parent-id']); - }); + expect(router.navigate).toHaveBeenCalledWith( + ['./', node.id], + jasmine.any(Object) + ); }); - describe('refresh on events', () => { - beforeEach(() => { - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); - spyOn(component.documentList, 'reload'); + it('should navigates to home when id not provided', () => { + component.navigate(); - fixture.detectChanges(); - }); - - it('should call refresh onContentCopied event if parent is the same', () => { - const nodes = [ - { entry: { parentId: '1' } }, - { entry: { parentId: '2' } } - ]; - - component.node = { id: '1' }; - - nodeActionsService.contentCopied.next(nodes); - - expect(component.documentList.reload).toHaveBeenCalled(); - }); - - it('should not call refresh onContentCopied event when parent mismatch', () => { - const nodes = [ - { entry: { parentId: '1' } }, - { entry: { parentId: '2' } } - ]; - - component.node = { id: '3' }; - - nodeActionsService.contentCopied.next(nodes); - - expect(component.documentList.reload).not.toHaveBeenCalled(); - }); - - it('should call refresh onCreateFolder event', () => { - contentManagementService.folderCreated.next(); - - expect(component.documentList.reload).toHaveBeenCalled(); - }); - - it('should call refresh editFolder event', () => { - contentManagementService.folderEdited.next(); - - expect(component.documentList.reload).toHaveBeenCalled(); - }); - - it('should call refresh deleteNode event', () => { - contentManagementService.nodesDeleted.next(); - - expect(component.documentList.reload).toHaveBeenCalled(); - }); - - it('should call refresh moveNode event', () => { - contentManagementService.nodesMoved.next(); - - expect(component.documentList.reload).toHaveBeenCalled(); - }); - - it('should call refresh restoreNode event', () => { - contentManagementService.nodesRestored.next(); - - expect(component.documentList.reload).toHaveBeenCalled(); - }); - - it('should call refresh on fileUploadComplete event if parent node match', fakeAsync(() => { - const file = { file: { options: { parentId: 'parentId' } } }; - component.node = { id: 'parentId' }; - - uploadService.fileUploadComplete.next(file); - - tick(500); - - expect(component.documentList.reload).toHaveBeenCalled(); - })); - - it('should not call refresh on fileUploadComplete event if parent mismatch', fakeAsync(() => { - const file = { file: { options: { parentId: 'otherId' } } }; - component.node = { id: 'parentId' }; - - uploadService.fileUploadComplete.next(file); - - tick(500); - - expect(component.documentList.reload).not.toHaveBeenCalled(); - })); - - it('should call refresh on fileUploadDeleted event if parent node match', fakeAsync(() => { - const file = { file: { options: { parentId: 'parentId' } } }; - component.node = { id: 'parentId' }; - - uploadService.fileUploadDeleted.next(file); - - tick(500); - - expect(component.documentList.reload).toHaveBeenCalled(); - })); - - it('should not call refresh on fileUploadDeleted event if parent mismatch', fakeAsync(() => { - const file: any = { file: { options: { parentId: 'otherId' } } }; - component.node = { id: 'parentId' }; - - uploadService.fileUploadDeleted.next(file); - - tick(500); - - expect(component.documentList.reload).not.toHaveBeenCalled(); - })); + expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object)); }); + it('should navigate home if node is root', () => { + component.node = { + path: { + elements: [{ id: 'node-id' }] + } + }; - describe('onBreadcrumbNavigate()', () => { - beforeEach(() => { - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); - fixture.detectChanges(); - }); + component.navigate(node.id); - it('should navigates to node id', () => { - const routeData: any = { id: 'some-where-over-the-rainbow' }; - spyOn(component, 'navigate'); + expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object)); + }); + }); - component.onBreadcrumbNavigate(routeData); + describe('isSiteContainer', () => { + it('should return false if node has no aspectNames', () => { + const mock = { aspectNames: [] }; - expect(component.navigate).toHaveBeenCalledWith(routeData.id); - }); + expect(component.isSiteContainer(mock)).toBe(false); }); - describe('Node navigation', () => { - beforeEach(() => { - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: node })); - spyOn(router, 'navigate'); + it('should return false if node is not site container', () => { + const mock = { aspectNames: ['something-else'] }; - fixture.detectChanges(); - }); - - it('should navigates to node when id provided', () => { - component.navigate(node.id); - - expect(router.navigate).toHaveBeenCalledWith(['./', node.id], jasmine.any(Object)); - }); - - it('should navigates to home when id not provided', () => { - component.navigate(); - - expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object)); - }); - - it('should navigate home if node is root', () => { - component.node = { - path: { - elements: [ {id: 'node-id'} ] - } - }; - - component.navigate(node.id); - - expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object)); - }); + expect(component.isSiteContainer(mock)).toBe(false); }); - describe('isSiteContainer', () => { - it('should return false if node has no aspectNames', () => { - const mock = { aspectNames: [] }; + it('should return true if node is a site container', () => { + const mock = { aspectNames: ['st:siteContainer'] }; - expect(component.isSiteContainer(mock)).toBe(false); - }); - - it('should return false if node is not site container', () => { - const mock = { aspectNames: ['something-else'] }; - - expect(component.isSiteContainer(mock)).toBe(false); - }); - - it('should return true if node is a site container', () => { - const mock = { aspectNames: [ 'st:siteContainer' ] }; - - expect(component.isSiteContainer(mock)).toBe(true); - }); + expect(component.isSiteContainer(mock)).toBe(true); }); + }); }); diff --git a/src/app/components/files/files.component.ts b/src/app/components/files/files.component.ts index 64aceab58..4971672a4 100644 --- a/src/app/components/files/files.component.ts +++ b/src/app/components/files/files.component.ts @@ -27,7 +27,12 @@ import { FileUploadEvent, UploadService } from '@alfresco/adf-core'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { Store } from '@ngrx/store'; -import { MinimalNodeEntity, MinimalNodeEntryEntity, PathElement, PathElementEntity } from 'alfresco-js-api'; +import { + MinimalNodeEntity, + MinimalNodeEntryEntity, + PathElement, + PathElementEntity +} from 'alfresco-js-api'; import { ContentManagementService } from '../../services/content-management.service'; import { NodeActionsService } from '../../services/node-actions.service'; import { AppStore } from '../../store/states/app.state'; @@ -39,238 +44,256 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { debounceTime } from 'rxjs/operators'; @Component({ - templateUrl: './files.component.html' + templateUrl: './files.component.html' }) export class FilesComponent extends PageComponent implements OnInit, OnDestroy { + isValidPath = true; + isSmallScreen = false; - isValidPath = true; - isSmallScreen = false; + private nodePath: PathElement[]; - private nodePath: PathElement[]; + constructor( + private router: Router, + private route: ActivatedRoute, + private contentApi: ContentApiService, + store: Store, + private nodeActionsService: NodeActionsService, + private uploadService: UploadService, + content: ContentManagementService, + extensions: AppExtensionService, + private breakpointObserver: BreakpointObserver + ) { + super(store, extensions, content); + } - constructor(private router: Router, - private route: ActivatedRoute, - private contentApi: ContentApiService, - store: Store, - private nodeActionsService: NodeActionsService, - private uploadService: UploadService, - content: ContentManagementService, - extensions: AppExtensionService, - private breakpointObserver: BreakpointObserver) { - super(store, extensions, content); - } + ngOnInit() { + super.ngOnInit(); - ngOnInit() { - super.ngOnInit(); + const { route, content, nodeActionsService, uploadService } = this; + const { data } = route.snapshot; - const { route, content, nodeActionsService, uploadService } = this; - const { data } = route.snapshot; + this.title = data.title; - this.title = data.title; + route.params.subscribe(({ folderId }: Params) => { + const nodeId = folderId || data.defaultNodeId; - route.params.subscribe(({ folderId }: Params) => { - const nodeId = folderId || data.defaultNodeId; + this.contentApi.getNode(nodeId).subscribe( + node => { + this.isValidPath = true; - this.contentApi - .getNode(nodeId) - .subscribe( - node => { - this.isValidPath = true; - - if (node.entry && node.entry.isFolder) { - this.updateCurrentNode(node.entry); - } else { - this.router.navigate( - ['/personal-files', node.entry.parentId], - { replaceUrl: true } - ); - } - }, - () => this.isValidPath = false - ); - }); - - this.subscriptions = this.subscriptions.concat([ - nodeActionsService.contentCopied.subscribe((nodes) => this.onContentCopied(nodes)), - content.folderCreated.subscribe(() => this.documentList.reload()), - content.folderEdited.subscribe(() => this.documentList.reload()), - content.nodesDeleted.subscribe(() => this.documentList.reload()), - content.nodesMoved.subscribe(() => this.documentList.reload()), - content.nodesRestored.subscribe(() => this.documentList.reload()), - uploadService.fileUploadComplete.pipe(debounceTime(300)).subscribe(file => this.onFileUploadedEvent(file)), - uploadService.fileUploadDeleted.pipe(debounceTime(300)).subscribe((file) => this.onFileUploadedEvent(file)), - - this.breakpointObserver - .observe([ - Breakpoints.HandsetPortrait, - Breakpoints.HandsetLandscape - ]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }) - ]); - } - - ngOnDestroy() { - super.ngOnDestroy(); - this.store.dispatch(new SetCurrentFolderAction(null)); - } - - navigate(nodeId: string = null) { - const commands = [ './' ]; - - if (nodeId && !this.isRootNode(nodeId)) { - commands.push(nodeId); - } - - this.router.navigate(commands, { - relativeTo: this.route.parent - }); - } - - navigateTo(node: MinimalNodeEntity) { - if (node && node.entry) { - const { id, isFolder } = node.entry; - - if (isFolder) { - this.navigate(id); - return; - } - - if (PageComponent.isLockedNode(node.entry)) { - event.preventDefault(); - return; - } - - this.showPreview(node); - } - } - - onBreadcrumbNavigate(route: PathElementEntity) { - // todo: review this approach once 5.2.3 is out - if (this.nodePath && this.nodePath.length > 2) { - if (this.nodePath[1].name === 'Sites' && this.nodePath[2].id === route.id) { - return this.navigate(this.nodePath[3].id); - } - } - this.navigate(route.id); - } - - onFileUploadedEvent(event: FileUploadEvent) { - const node: MinimalNodeEntity = event.file.data; - - // check root and child nodes - if (node && node.entry && node.entry.parentId === this.getParentNodeId()) { - this.documentList.reload(); - return; - } - - // check the child nodes to show dropped folder - if (event && event.file.options.parentId === this.getParentNodeId()) { - this.displayFolderParent(event.file.options.path, 0); - return; - } - - if (event && event.file.options.parentId) { - if (this.nodePath) { - const correspondingNodePath = this.nodePath.find(pathItem => pathItem.id === event.file.options.parentId); - - // check if the current folder has the 'trigger-upload-folder' as one of its parents - if (correspondingNodePath) { - const correspondingIndex = this.nodePath.length - this.nodePath.indexOf(correspondingNodePath); - this.displayFolderParent(event.file.options.path, correspondingIndex); - } - } - } - } - - displayFolderParent(filePath = '', index) { - const parentName = filePath.split('/')[index]; - const currentFoldersDisplayed: any = this.documentList.data.getRows() || []; - - const alreadyDisplayedParentFolder = currentFoldersDisplayed.find( - row => row.node.entry.isFolder && row.node.entry.name === parentName); - - if (alreadyDisplayedParentFolder) { - return; - } - this.documentList.reload(); - } - - onContentCopied(nodes: MinimalNodeEntity[]) { - const newNode = nodes - .find((node) => { - return node && node.entry && node.entry.parentId === this.getParentNodeId(); + if (node.entry && node.entry.isFolder) { + this.updateCurrentNode(node.entry); + } else { + this.router.navigate(['/personal-files', node.entry.parentId], { + replaceUrl: true }); - if (newNode) { - this.documentList.reload(); - } + } + }, + () => (this.isValidPath = false) + ); + }); + + this.subscriptions = this.subscriptions.concat([ + nodeActionsService.contentCopied.subscribe(nodes => + this.onContentCopied(nodes) + ), + content.folderCreated.subscribe(() => this.documentList.reload()), + content.folderEdited.subscribe(() => this.documentList.reload()), + content.nodesDeleted.subscribe(() => this.documentList.reload()), + content.nodesMoved.subscribe(() => this.documentList.reload()), + content.nodesRestored.subscribe(() => this.documentList.reload()), + uploadService.fileUploadComplete + .pipe(debounceTime(300)) + .subscribe(file => this.onFileUploadedEvent(file)), + uploadService.fileUploadDeleted + .pipe(debounceTime(300)) + .subscribe(file => this.onFileUploadedEvent(file)), + + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ]); + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.store.dispatch(new SetCurrentFolderAction(null)); + } + + navigate(nodeId: string = null) { + const commands = ['./']; + + if (nodeId && !this.isRootNode(nodeId)) { + commands.push(nodeId); } + this.router.navigate(commands, { + relativeTo: this.route.parent + }); + } + + navigateTo(node: MinimalNodeEntity) { + if (node && node.entry) { + const { id, isFolder } = node.entry; + + if (isFolder) { + this.navigate(id); + return; + } + + if (PageComponent.isLockedNode(node.entry)) { + event.preventDefault(); + return; + } + + this.showPreview(node); + } + } + + onBreadcrumbNavigate(route: PathElementEntity) { // todo: review this approach once 5.2.3 is out - private async updateCurrentNode(node: MinimalNodeEntryEntity) { - this.nodePath = null; + if (this.nodePath && this.nodePath.length > 2) { + if ( + this.nodePath[1].name === 'Sites' && + this.nodePath[2].id === route.id + ) { + return this.navigate(this.nodePath[3].id); + } + } + this.navigate(route.id); + } - if (node && node.path && node.path.elements) { - const elements = node.path.elements; + onFileUploadedEvent(event: FileUploadEvent) { + const node: MinimalNodeEntity = event.file.data; - this.nodePath = elements.map(pathElement => { - return Object.assign({}, pathElement); - }); - - if (elements.length > 1) { - if (elements[1].name === 'User Homes') { - elements.splice(0, 2); - } else if (elements[1].name === 'Sites') { - await this.normalizeSitePath(node); - } - } - } - - this.node = node; - this.store.dispatch(new SetCurrentFolderAction(node)); + // check root and child nodes + if (node && node.entry && node.entry.parentId === this.getParentNodeId()) { + this.documentList.reload(); + return; } - // todo: review this approach once 5.2.3 is out - private async normalizeSitePath(node: MinimalNodeEntryEntity) { - const elements = node.path.elements; - - // remove 'Sites' - elements.splice(1, 1); - - if (this.isSiteContainer(node)) { - // rename 'documentLibrary' entry to the target site display name - // clicking on the breadcrumb entry loads the site content - const parentNode = await this.contentApi.getNodeInfo(node.parentId).toPromise(); - node.name = parentNode.properties['cm:title'] || parentNode.name; - - // remove the site entry - elements.splice(1, 1); - } else { - // remove 'documentLibrary' in the middle of the path - const docLib = elements.findIndex(el => el.name === 'documentLibrary'); - if (docLib > -1) { - const siteFragment = elements[docLib - 1]; - const siteNode = await this.contentApi.getNodeInfo(siteFragment.id).toPromise(); - - // apply Site Name to the parent fragment - siteFragment.name = siteNode.properties['cm:title'] || siteNode.name; - elements.splice(docLib, 1); - } - } + // check the child nodes to show dropped folder + if (event && event.file.options.parentId === this.getParentNodeId()) { + this.displayFolderParent(event.file.options.path, 0); + return; } - isSiteContainer(node: MinimalNodeEntryEntity): boolean { - if (node && node.aspectNames && node.aspectNames.length > 0) { - return node.aspectNames.indexOf('st:siteContainer') >= 0; + if (event && event.file.options.parentId) { + if (this.nodePath) { + const correspondingNodePath = this.nodePath.find( + pathItem => pathItem.id === event.file.options.parentId + ); + + // check if the current folder has the 'trigger-upload-folder' as one of its parents + if (correspondingNodePath) { + const correspondingIndex = + this.nodePath.length - this.nodePath.indexOf(correspondingNodePath); + this.displayFolderParent(event.file.options.path, correspondingIndex); } - return false; + } + } + } + + displayFolderParent(filePath = '', index) { + const parentName = filePath.split('/')[index]; + const currentFoldersDisplayed: any = this.documentList.data.getRows() || []; + + const alreadyDisplayedParentFolder = currentFoldersDisplayed.find( + row => row.node.entry.isFolder && row.node.entry.name === parentName + ); + + if (alreadyDisplayedParentFolder) { + return; + } + this.documentList.reload(); + } + + onContentCopied(nodes: MinimalNodeEntity[]) { + const newNode = nodes.find(node => { + return ( + node && node.entry && node.entry.parentId === this.getParentNodeId() + ); + }); + if (newNode) { + this.documentList.reload(); + } + } + + // todo: review this approach once 5.2.3 is out + private async updateCurrentNode(node: MinimalNodeEntryEntity) { + this.nodePath = null; + + if (node && node.path && node.path.elements) { + const elements = node.path.elements; + + this.nodePath = elements.map(pathElement => { + return Object.assign({}, pathElement); + }); + + if (elements.length > 1) { + if (elements[1].name === 'User Homes') { + elements.splice(0, 2); + } else if (elements[1].name === 'Sites') { + await this.normalizeSitePath(node); + } + } } - isRootNode(nodeId: string): boolean { - if (this.node && this.node.path && this.node.path.elements && this.node.path.elements.length > 0) { - return this.node.path.elements[0].id === nodeId; - } - return false; + this.node = node; + this.store.dispatch(new SetCurrentFolderAction(node)); + } + + // todo: review this approach once 5.2.3 is out + private async normalizeSitePath(node: MinimalNodeEntryEntity) { + const elements = node.path.elements; + + // remove 'Sites' + elements.splice(1, 1); + + if (this.isSiteContainer(node)) { + // rename 'documentLibrary' entry to the target site display name + // clicking on the breadcrumb entry loads the site content + const parentNode = await this.contentApi + .getNodeInfo(node.parentId) + .toPromise(); + node.name = parentNode.properties['cm:title'] || parentNode.name; + + // remove the site entry + elements.splice(1, 1); + } else { + // remove 'documentLibrary' in the middle of the path + const docLib = elements.findIndex(el => el.name === 'documentLibrary'); + if (docLib > -1) { + const siteFragment = elements[docLib - 1]; + const siteNode = await this.contentApi + .getNodeInfo(siteFragment.id) + .toPromise(); + + // apply Site Name to the parent fragment + siteFragment.name = siteNode.properties['cm:title'] || siteNode.name; + elements.splice(docLib, 1); + } } + } + + isSiteContainer(node: MinimalNodeEntryEntity): boolean { + if (node && node.aspectNames && node.aspectNames.length > 0) { + return node.aspectNames.indexOf('st:siteContainer') >= 0; + } + return false; + } + + isRootNode(nodeId: string): boolean { + if ( + this.node && + this.node.path && + this.node.path.elements && + this.node.path.elements.length > 0 + ) { + return this.node.path.elements[0].id === nodeId; + } + return false; + } } diff --git a/src/app/components/generic-error/generic-error.component.theme.scss b/src/app/components/generic-error/generic-error.component.theme.scss index bbf0735e9..d921cc072 100644 --- a/src/app/components/generic-error/generic-error.component.theme.scss +++ b/src/app/components/generic-error/generic-error.component.theme.scss @@ -1,27 +1,27 @@ @mixin aca-generic-error-theme($theme) { - $warn: map-get($theme, warn); - $foreground: map-get($theme, foreground); + $warn: map-get($theme, warn); + $foreground: map-get($theme, foreground); - .aca-generic-error { - color: mat-color($foreground, text, 0.54); + .aca-generic-error { + color: mat-color($foreground, text, 0.54); - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - width: 100%; - height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + width: 100%; + height: 100%; - &__title { - font-size: 16px; - } - - mat-icon { - color: mat-color($warn); - direction: rtl; - font-size: 52px; - height: 52px; - width: 52px; - } + &__title { + font-size: 16px; } + + mat-icon { + color: mat-color($warn); + direction: rtl; + font-size: 52px; + height: 52px; + width: 52px; + } + } } diff --git a/src/app/components/generic-error/generic-error.component.ts b/src/app/components/generic-error/generic-error.component.ts index dfae6b91e..c881d1f77 100644 --- a/src/app/components/generic-error/generic-error.component.ts +++ b/src/app/components/generic-error/generic-error.component.ts @@ -23,14 +23,17 @@ * along with Alfresco. If not, see . */ -import { Component, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core'; +import { + Component, + ViewEncapsulation, + ChangeDetectionStrategy +} from '@angular/core'; @Component({ - selector: 'aca-generic-error', - templateUrl: './generic-error.component.html', - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'aca-generic-error' } + selector: 'aca-generic-error', + templateUrl: './generic-error.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'aca-generic-error' } }) export class GenericErrorComponent {} - diff --git a/src/app/components/info-drawer/comments-tab/comments-tab.component.ts b/src/app/components/info-drawer/comments-tab/comments-tab.component.ts index 86b77d9aa..a02e5a19d 100644 --- a/src/app/components/info-drawer/comments-tab/comments-tab.component.ts +++ b/src/app/components/info-drawer/comments-tab/comments-tab.component.ts @@ -28,18 +28,18 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api'; import { NodePermissionService } from '../../../services/node-permission.service'; @Component({ - selector: 'app-comments-tab', - template: ` + selector: 'app-comments-tab', + template: ` ` }) export class CommentsTabComponent { - @Input() - node: MinimalNodeEntryEntity; + @Input() + node: MinimalNodeEntryEntity; - constructor(private permission: NodePermissionService) {} + constructor(private permission: NodePermissionService) {} - get canUpdateNode() { - return this.node && this.permission.check(this.node, ['update']); - } + get canUpdateNode() { + return this.node && this.permission.check(this.node, ['update']); + } } diff --git a/src/app/components/info-drawer/info-drawer.component.ts b/src/app/components/info-drawer/info-drawer.component.ts index 2974b7e9f..d5d77767e 100644 --- a/src/app/components/info-drawer/info-drawer.component.ts +++ b/src/app/components/info-drawer/info-drawer.component.ts @@ -30,71 +30,73 @@ import { AppExtensionService } from '../../extensions/extension.service'; import { SidebarTabRef } from '@alfresco/adf-extensions'; @Component({ - selector: 'aca-info-drawer', - templateUrl: './info-drawer.component.html' + selector: 'aca-info-drawer', + templateUrl: './info-drawer.component.html' }) export class InfoDrawerComponent implements OnChanges, OnInit { - @Input() nodeId: string; - @Input() node: MinimalNodeEntity; + @Input() + nodeId: string; + @Input() + node: MinimalNodeEntity; - isLoading = false; - displayNode: MinimalNodeEntryEntity; - tabs: Array = []; + isLoading = false; + displayNode: MinimalNodeEntryEntity; + tabs: Array = []; - constructor( - private contentApi: ContentApiService, - private extensions: AppExtensionService - ) {} + constructor( + private contentApi: ContentApiService, + private extensions: AppExtensionService + ) {} - ngOnInit() { - this.tabs = this.extensions.getSidebarTabs(); - } + ngOnInit() { + this.tabs = this.extensions.getSidebarTabs(); + } - ngOnChanges() { - if (this.node) { - const entry = this.node.entry; - if (entry.nodeId) { - this.loadNodeInfo(entry.nodeId); - } else if ((entry).guid) { - // workaround for Favorite files - this.loadNodeInfo(entry.id); - } else { - // workaround Recent - if (this.isTypeImage(entry) && !this.hasAspectNames(entry)) { - this.loadNodeInfo(this.node.entry.id); - } else { - this.setDisplayNode(this.node.entry); - } - } + ngOnChanges() { + if (this.node) { + const entry = this.node.entry; + if (entry.nodeId) { + this.loadNodeInfo(entry.nodeId); + } else if ((entry).guid) { + // workaround for Favorite files + this.loadNodeInfo(entry.id); + } else { + // workaround Recent + if (this.isTypeImage(entry) && !this.hasAspectNames(entry)) { + this.loadNodeInfo(this.node.entry.id); + } else { + this.setDisplayNode(this.node.entry); } + } } + } - private hasAspectNames(entry: MinimalNodeEntryEntity): boolean { - return entry.aspectNames && entry.aspectNames.includes('exif:exif'); + private hasAspectNames(entry: MinimalNodeEntryEntity): boolean { + return entry.aspectNames && entry.aspectNames.includes('exif:exif'); + } + + private isTypeImage(entry: MinimalNodeEntryEntity): boolean { + if (entry && entry.content && entry.content.mimeType) { + return entry.content.mimeType.includes('image/'); } + return false; + } - private isTypeImage(entry: MinimalNodeEntryEntity): boolean { - if (entry && entry.content && entry.content.mimeType) { - return entry.content.mimeType.includes('image/'); - } - return false; + private loadNodeInfo(nodeId: string) { + if (nodeId) { + this.isLoading = true; + + this.contentApi.getNodeInfo(nodeId).subscribe( + entity => { + this.setDisplayNode(entity); + this.isLoading = false; + }, + () => (this.isLoading = false) + ); } + } - private loadNodeInfo(nodeId: string) { - if (nodeId) { - this.isLoading = true; - - this.contentApi.getNodeInfo(nodeId).subscribe( - entity => { - this.setDisplayNode(entity); - this.isLoading = false; - }, - () => this.isLoading = false - ); - } - } - - private setDisplayNode(node: MinimalNodeEntryEntity) { - this.displayNode = node; - } + private setDisplayNode(node: MinimalNodeEntryEntity) { + this.displayNode = node; + } } diff --git a/src/app/components/info-drawer/info.drawer.module.ts b/src/app/components/info-drawer/info.drawer.module.ts index 29c32ab90..f11dabd5c 100644 --- a/src/app/components/info-drawer/info.drawer.module.ts +++ b/src/app/components/info-drawer/info.drawer.module.ts @@ -24,8 +24,8 @@ */ import { - ContentMetadataModule, - VersionManagerModule + ContentMetadataModule, + VersionManagerModule } from '@alfresco/adf-content-services'; import { CoreModule } from '@alfresco/adf-core'; import { ExtensionsModule } from '@alfresco/adf-extensions'; @@ -39,26 +39,26 @@ import { MetadataTabComponent } from './metadata-tab/metadata-tab.component'; import { VersionsTabComponent } from './versions-tab/versions-tab.component'; export function components() { - return [ - InfoDrawerComponent, - MetadataTabComponent, - CommentsTabComponent, - VersionsTabComponent - ]; + return [ + InfoDrawerComponent, + MetadataTabComponent, + CommentsTabComponent, + VersionsTabComponent + ]; } @NgModule({ - imports: [ - CommonModule, - MaterialModule, - CoreModule.forChild(), - ExtensionsModule.forChild(), - ContentMetadataModule, - VersionManagerModule, - DirectivesModule - ], - declarations: [...components()], - exports: [...components()], - entryComponents: [...components()] + imports: [ + CommonModule, + MaterialModule, + CoreModule.forChild(), + ExtensionsModule.forChild(), + ContentMetadataModule, + VersionManagerModule, + DirectivesModule + ], + declarations: [...components()], + exports: [...components()], + entryComponents: [...components()] }) export class AppInfoDrawerModule {} diff --git a/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts b/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts index 5dea7a661..19584211b 100644 --- a/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts +++ b/src/app/components/info-drawer/metadata-tab/metadata-tab.component.ts @@ -28,25 +28,25 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api'; import { NodePermissionService } from '../../../services/node-permission.service'; @Component({ - selector: 'app-metadata-tab', - template: ` - - + selector: 'app-metadata-tab', + template: ` + + `, - encapsulation: ViewEncapsulation.None, - host: { 'class': 'app-metadata-tab' } + encapsulation: ViewEncapsulation.None, + host: { class: 'app-metadata-tab' } }) export class MetadataTabComponent { - @Input() - node: MinimalNodeEntryEntity; + @Input() + node: MinimalNodeEntryEntity; - constructor(private permission: NodePermissionService) {} + constructor(private permission: NodePermissionService) {} - get canUpdateNode() { - return this.node && this.permission.check(this.node, ['update']); - } + get canUpdateNode() { + return this.node && this.permission.check(this.node, ['update']); + } } diff --git a/src/app/components/info-drawer/versions-tab/versions-tab.component.ts b/src/app/components/info-drawer/versions-tab/versions-tab.component.ts index 2bfe0751c..1a7faddb1 100644 --- a/src/app/components/info-drawer/versions-tab/versions-tab.component.ts +++ b/src/app/components/info-drawer/versions-tab/versions-tab.component.ts @@ -27,8 +27,8 @@ import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; @Component({ - selector: 'app-versions-tab', - template: ` + selector: 'app-versions-tab', + template: ` { - let fixture: ComponentFixture; - let component: LayoutComponent; - let appConfig: AppConfigService; - let userPreference: UserPreferencesService; + let fixture: ComponentFixture; + let component: LayoutComponent; + let appConfig: AppConfigService; + let userPreference: UserPreferencesService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ AppTestingModule ], - declarations: [ - LayoutComponent, - SidenavViewsManagerDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); - - fixture = TestBed.createComponent(LayoutComponent); - component = fixture.componentInstance; - appConfig = TestBed.get(AppConfigService); - userPreference = TestBed.get(UserPreferencesService); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [LayoutComponent, SidenavViewsManagerDirective], + schemas: [NO_ERRORS_SCHEMA] }); - describe('sidenav state', () => { - it('should get state from configuration', () => { - appConfig.config = { - sideNav: { - expandedSidenav: false, - preserveState: false - } - }; + fixture = TestBed.createComponent(LayoutComponent); + component = fixture.componentInstance; + appConfig = TestBed.get(AppConfigService); + userPreference = TestBed.get(UserPreferencesService); + }); - fixture.detectChanges(); + describe('sidenav state', () => { + it('should get state from configuration', () => { + appConfig.config = { + sideNav: { + expandedSidenav: false, + preserveState: false + } + }; - expect(component.expandedSidenav).toBe(false); - }); + fixture.detectChanges(); - it('should resolve state to true is no configuration', () => { - appConfig.config = {}; - - fixture.detectChanges(); - - expect(component.expandedSidenav).toBe(true); - }); - - it('should get state from user settings as true', () => { - appConfig.config = { - sideNav: { - expandedSidenav: false, - preserveState: true - } - }; - - spyOn(userPreference, 'get').and.callFake(key => { - if (key === 'expandedSidenav') { - return 'true'; - } - }); - - fixture.detectChanges(); - - expect(component.expandedSidenav).toBe(true); - }); - - it('should get state from user settings as false', () => { - appConfig.config = { - sideNav: { - expandedSidenav: false, - preserveState: true - } - }; - - spyOn(userPreference, 'get').and.callFake(key => { - if (key === 'expandedSidenav') { - return 'false'; - } - }); - - fixture.detectChanges(); - - expect(component.expandedSidenav).toBe(false); - }); + expect(component.expandedSidenav).toBe(false); }); - }); + + it('should resolve state to true is no configuration', () => { + appConfig.config = {}; + + fixture.detectChanges(); + + expect(component.expandedSidenav).toBe(true); + }); + + it('should get state from user settings as true', () => { + appConfig.config = { + sideNav: { + expandedSidenav: false, + preserveState: true + } + }; + + spyOn(userPreference, 'get').and.callFake(key => { + if (key === 'expandedSidenav') { + return 'true'; + } + }); + + fixture.detectChanges(); + + expect(component.expandedSidenav).toBe(true); + }); + + it('should get state from user settings as false', () => { + appConfig.config = { + sideNav: { + expandedSidenav: false, + preserveState: true + } + }; + + spyOn(userPreference, 'get').and.callFake(key => { + if (key === 'expandedSidenav') { + return 'false'; + } + }); + + fixture.detectChanges(); + + expect(component.expandedSidenav).toBe(false); + }); + }); +}); diff --git a/src/app/components/layout/layout.component.ts b/src/app/components/layout/layout.component.ts index a5e8ab13b..44431905e 100644 --- a/src/app/components/layout/layout.component.ts +++ b/src/app/components/layout/layout.component.ts @@ -24,11 +24,11 @@ */ import { - Component, - OnInit, - OnDestroy, - ViewChild, - ViewEncapsulation + Component, + OnInit, + OnDestroy, + ViewChild, + ViewEncapsulation } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; @@ -37,76 +37,72 @@ import { SidenavViewsManagerDirective } from './sidenav-views-manager.directive' import { Store } from '@ngrx/store'; import { AppStore } from '../../store/states'; import { - currentFolder, - selectAppName, - selectHeaderColor, - selectLogoPath + currentFolder, + selectAppName, + selectHeaderColor, + selectLogoPath } from '../../store/selectors/app.selectors'; import { takeUntil } from 'rxjs/operators'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; @Component({ - selector: 'app-layout', - templateUrl: './layout.component.html', - styleUrls: ['./layout.component.scss'], - encapsulation: ViewEncapsulation.None, - host: { class: 'app-layout' } + selector: 'app-layout', + templateUrl: './layout.component.html', + styleUrls: ['./layout.component.scss'], + encapsulation: ViewEncapsulation.None, + host: { class: 'app-layout' } }) export class LayoutComponent implements OnInit, OnDestroy { - @ViewChild(SidenavViewsManagerDirective) - manager: SidenavViewsManagerDirective; + @ViewChild(SidenavViewsManagerDirective) + manager: SidenavViewsManagerDirective; - onDestroy$: Subject = new Subject(); - expandedSidenav: boolean; - node: MinimalNodeEntryEntity; - canUpload = false; + onDestroy$: Subject = new Subject(); + expandedSidenav: boolean; + node: MinimalNodeEntryEntity; + canUpload = false; - appName$: Observable; - headerColor$: Observable; - logo$: Observable; + appName$: Observable; + headerColor$: Observable; + logo$: Observable; - isSmallScreen = false; + isSmallScreen = false; - constructor( - protected store: Store, - private permission: NodePermissionService, - private breakpointObserver: BreakpointObserver - ) { - this.headerColor$ = store.select(selectHeaderColor); - this.appName$ = store.select(selectAppName); - this.logo$ = store.select(selectLogoPath); + constructor( + protected store: Store, + private permission: NodePermissionService, + private breakpointObserver: BreakpointObserver + ) { + this.headerColor$ = store.select(selectHeaderColor); + this.appName$ = store.select(selectAppName); + this.logo$ = store.select(selectLogoPath); + } + + ngOnInit() { + if (!this.manager.minimizeSidenav) { + this.expandedSidenav = this.manager.sidenavState; + } else { + this.expandedSidenav = false; } - ngOnInit() { - if (!this.manager.minimizeSidenav) { - this.expandedSidenav = this.manager.sidenavState; - } else { - this.expandedSidenav = false; - } + this.manager.run(true); - this.manager.run(true); + this.store + .select(currentFolder) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(node => { + this.node = node; + this.canUpload = node && this.permission.check(node, ['create']); + }); - this.store - .select(currentFolder) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(node => { - this.node = node; - this.canUpload = - node && this.permission.check(node, ['create']); - }); + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }); + } - this.breakpointObserver - .observe([ - Breakpoints.HandsetPortrait, - Breakpoints.HandsetLandscape - ]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }); - } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } } diff --git a/src/app/components/layout/sidenav-views-manager.directive.ts b/src/app/components/layout/sidenav-views-manager.directive.ts index 0519f9038..64848a0cc 100644 --- a/src/app/components/layout/sidenav-views-manager.directive.ts +++ b/src/app/components/layout/sidenav-views-manager.directive.ts @@ -1,93 +1,92 @@ import { Directive, ContentChild } from '@angular/core'; import { Router, NavigationEnd } from '@angular/router'; import { - UserPreferencesService, - AppConfigService, - SidenavLayoutComponent + UserPreferencesService, + AppConfigService, + SidenavLayoutComponent } from '@alfresco/adf-core'; import { filter } from 'rxjs/operators'; @Directive({ - selector: '[acaSidenavManager]', - exportAs: 'acaSidenavManager' + selector: '[acaSidenavManager]', + exportAs: 'acaSidenavManager' }) export class SidenavViewsManagerDirective { - @ContentChild(SidenavLayoutComponent) sidenavLayout: SidenavLayoutComponent; + @ContentChild(SidenavLayoutComponent) + sidenavLayout: SidenavLayoutComponent; - minimizeSidenav = false; - hideSidenav = false; + minimizeSidenav = false; + hideSidenav = false; - private _run = false; - private minimizeConditions: string[] = ['search']; - private hideConditions: string[] = ['preview']; + private _run = false; + private minimizeConditions: string[] = ['search']; + private hideConditions: string[] = ['preview']; - constructor( - private router: Router, - private userPreferenceService: UserPreferencesService, - private appConfigService: AppConfigService + constructor( + private router: Router, + private userPreferenceService: UserPreferencesService, + private appConfigService: AppConfigService + ) { + this.router.events + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe((event: any) => { + this.minimizeSidenav = this.minimizeConditions.some(el => + event.urlAfterRedirects.includes(el) + ); + this.hideSidenav = this.hideConditions.some(el => + event.urlAfterRedirects.includes(el) + ); + + if (this._run) { + this.manageSidenavState(); + } + }); + } + + run(shouldRun) { + this._run = shouldRun; + } + + manageSidenavState() { + if (this.minimizeSidenav && !this.sidenavLayout.isMenuMinimized) { + this.sidenavLayout.isMenuMinimized = true; + this.sidenavLayout.container.toggleMenu(); + } + + if (!this.minimizeSidenav) { + if (this.sidenavState && this.sidenavLayout.isMenuMinimized) { + this.sidenavLayout.isMenuMinimized = false; + this.sidenavLayout.container.toggleMenu(); + } + } + } + + setState(state) { + if ( + !this.minimizeSidenav && + this.appConfigService.get('sideNav.preserveState') ) { - this.router.events - .pipe(filter(event => event instanceof NavigationEnd)) - .subscribe((event: any) => { - this.minimizeSidenav = this.minimizeConditions.some(el => - event.urlAfterRedirects.includes(el) - ); - this.hideSidenav = this.hideConditions.some(el => - event.urlAfterRedirects.includes(el) - ); + this.userPreferenceService.set('expandedSidenav', state); + } + } - if (this._run) { - this.manageSidenavState(); - } - }); + get sidenavState(): boolean { + const expand = this.appConfigService.get( + 'sideNav.expandedSidenav', + true + ); + const preserveState = this.appConfigService.get( + 'sideNav.preserveState', + true + ); + + if (preserveState) { + return ( + this.userPreferenceService.get('expandedSidenav', expand.toString()) === + 'true' + ); } - run(shouldRun) { - this._run = shouldRun; - } - - manageSidenavState() { - if (this.minimizeSidenav && !this.sidenavLayout.isMenuMinimized) { - this.sidenavLayout.isMenuMinimized = true; - this.sidenavLayout.container.toggleMenu(); - } - - if (!this.minimizeSidenav) { - if (this.sidenavState && this.sidenavLayout.isMenuMinimized) { - this.sidenavLayout.isMenuMinimized = false; - this.sidenavLayout.container.toggleMenu(); - } - } - } - - setState(state) { - if ( - !this.minimizeSidenav && - this.appConfigService.get('sideNav.preserveState') - ) { - this.userPreferenceService.set('expandedSidenav', state); - } - } - - get sidenavState(): boolean { - const expand = this.appConfigService.get( - 'sideNav.expandedSidenav', - true - ); - const preserveState = this.appConfigService.get( - 'sideNav.preserveState', - true - ); - - if (preserveState) { - return ( - this.userPreferenceService.get( - 'expandedSidenav', - expand.toString() - ) === 'true' - ); - } - - return expand; - } + return expand; + } } diff --git a/src/app/components/libraries/libraries.component.spec.ts b/src/app/components/libraries/libraries.component.spec.ts index ca85bc7fd..2c31f6d39 100644 --- a/src/app/components/libraries/libraries.component.spec.ts +++ b/src/app/components/libraries/libraries.component.spec.ts @@ -28,8 +28,12 @@ import { of } from 'rxjs'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router } from '@angular/router'; import { - AlfrescoApiService, - TimeAgoPipe, NodeNameTooltipPipe, NodeFavoriteDirective, DataTableComponent, AppConfigPipe + AlfrescoApiService, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DataTableComponent, + AppConfigPipe } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ShareDataTableAdapter } from '@alfresco/adf-content-services'; @@ -39,149 +43,157 @@ import { ContentApiService } from '../../services/content-api.service'; import { ExperimentalDirective } from '../../directives/experimental.directive'; describe('LibrariesComponent', () => { - let fixture: ComponentFixture; - let component: LibrariesComponent; - let contentApi: ContentApiService; - let alfrescoApi: AlfrescoApiService; - let router: Router; - let page; - let node; + let fixture: ComponentFixture; + let component: LibrariesComponent; + let contentApi: ContentApiService; + let alfrescoApi: AlfrescoApiService; + let router: Router; + let page; + let node; + + beforeEach(() => { + page = { + list: { + entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }], + pagination: { data: 'data' } + } + }; + + node = { + id: 'nodeId', + path: { + elements: [] + } + }; + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + DataTableComponent, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DocumentListComponent, + LibrariesComponent, + AppConfigPipe, + ExperimentalDirective + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(LibrariesComponent); + component = fixture.componentInstance; + + alfrescoApi = TestBed.get(AlfrescoApiService); + alfrescoApi.reset(); + router = TestBed.get(Router); + + spyOn(alfrescoApi.sitesApi, 'getSites').and.returnValue( + Promise.resolve(page) + ); + spyOn(alfrescoApi.peopleApi, 'getSiteMembership').and.returnValue( + Promise.resolve({}) + ); + + contentApi = TestBed.get(ContentApiService); + }); + + describe('makeLibraryTooltip()', () => { + it('maps tooltip to description', () => { + node.description = 'description'; + const tooltip = component.makeLibraryTooltip(node); + + expect(tooltip).toBe(node.description); + }); + + it('maps tooltip to description', () => { + node.title = 'title'; + const tooltip = component.makeLibraryTooltip(node); + + expect(tooltip).toBe(node.title); + }); + + it('sets tooltip to empty string', () => { + const tooltip = component.makeLibraryTooltip(node); + + expect(tooltip).toBe(''); + }); + }); + + describe('makeLibraryTitle()', () => { + it('sets title with id when duplicate nodes title exists in list', () => { + node.title = 'title'; + + const data = new ShareDataTableAdapter(null, null); + data.setRows([ + { node: { entry: { id: 'some-id', title: 'title' } } } + ]); + + component.documentList.data = data; + + const title = component.makeLibraryTitle(node); + expect(title).toContain('nodeId'); + }); + + it('sets title when no duplicate nodes title exists in list', () => { + node.title = 'title'; + + const data = new ShareDataTableAdapter(null, null); + data.setRows([ + { node: { entry: { id: 'some-id', title: 'title-some-id' } } } + ]); + + component.documentList.data = data; + const title = component.makeLibraryTitle(node); + + expect(title).toBe('title'); + }); + }); + + describe('Node navigation', () => { + let routerSpy; beforeEach(() => { - page = { - list: { - entries: [ { entry: { id: 1 } }, { entry: { id: 2 } } ], - pagination: { data: 'data'} - } - }; - - node = { - id: 'nodeId', - path: { - elements: [] - } - }; + routerSpy = spyOn(router, 'navigate'); }); - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ AppTestingModule ], - declarations: [ - DataTableComponent, - TimeAgoPipe, - NodeNameTooltipPipe, - NodeFavoriteDirective, - DocumentListComponent, - LibrariesComponent, - AppConfigPipe, - ExperimentalDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); + it('does not navigate when id is not passed', () => { + component.navigate(null); - fixture = TestBed.createComponent(LibrariesComponent); - component = fixture.componentInstance; - - alfrescoApi = TestBed.get(AlfrescoApiService); - alfrescoApi.reset(); - router = TestBed.get(Router); - - spyOn(alfrescoApi.sitesApi, 'getSites').and.returnValue((Promise.resolve(page))); - spyOn(alfrescoApi.peopleApi, 'getSiteMembership').and.returnValue((Promise.resolve({}))); - - contentApi = TestBed.get(ContentApiService); + expect(router.navigate).not.toHaveBeenCalled(); }); - describe('makeLibraryTooltip()', () => { - it('maps tooltip to description', () => { - node.description = 'description'; - const tooltip = component.makeLibraryTooltip(node); + it('navigates to node id', () => { + const document = { id: 'documentId' }; + spyOn(contentApi, 'getNode').and.returnValue(of({ entry: document })); - expect(tooltip).toBe(node.description); - }); + component.navigate(node.id); - it('maps tooltip to description', () => { - node.title = 'title'; - const tooltip = component.makeLibraryTooltip(node); + expect(routerSpy.calls.argsFor(0)[0]).toEqual(['./', document.id]); + }); + }); - expect(tooltip).toBe(node.title); - }); + describe('navigateTo', () => { + it('navigates into library folder', () => { + spyOn(component, 'navigate'); - it('sets tooltip to empty string', () => { - const tooltip = component.makeLibraryTooltip(node); + const site: any = { + entry: { guid: 'node-guid' } + }; - expect(tooltip).toBe(''); - }); + component.navigateTo(site); + + expect(component.navigate).toHaveBeenCalledWith('node-guid'); }); - describe('makeLibraryTitle()', () => { - it('sets title with id when duplicate nodes title exists in list', () => { - node.title = 'title'; + it(' does not navigate when library is not provided', () => { + spyOn(component, 'navigate'); - const data = new ShareDataTableAdapter(null, null); - data.setRows([{ node: { entry: { id: 'some-id', title: 'title' } } }]); + component.navigateTo(null); - component.documentList.data = data; - - const title = component.makeLibraryTitle(node); - expect(title).toContain('nodeId'); - }); - - it('sets title when no duplicate nodes title exists in list', () => { - node.title = 'title'; - - const data = new ShareDataTableAdapter(null, null); - data.setRows([{ node: { entry: { id: 'some-id', title: 'title-some-id' } } }]); - - component.documentList.data = data; - const title = component.makeLibraryTitle(node); - - expect(title).toBe('title'); - }); - }); - - describe('Node navigation', () => { - let routerSpy; - - beforeEach(() => { - routerSpy = spyOn(router, 'navigate'); - }); - - it('does not navigate when id is not passed', () => { - component.navigate(null); - - expect(router.navigate).not.toHaveBeenCalled(); - }); - - it('navigates to node id', () => { - const document = { id: 'documentId' }; - spyOn(contentApi, 'getNode').and.returnValue(of({ entry: document })); - - component.navigate(node.id); - - expect(routerSpy.calls.argsFor(0)[0]).toEqual(['./', document.id]); - }); - }); - - describe('navigateTo', () => { - it('navigates into library folder', () => { - spyOn(component, 'navigate'); - - const site: any = { - entry: { guid: 'node-guid' } - }; - - component.navigateTo(site); - - expect(component.navigate).toHaveBeenCalledWith('node-guid'); - }); - - it(' does not navigate when library is not provided', () => { - spyOn(component, 'navigate'); - - component.navigateTo(null); - - expect(component.navigate).not.toHaveBeenCalled(); - }); + expect(component.navigate).not.toHaveBeenCalled(); }); + }); }); diff --git a/src/app/components/libraries/libraries.component.ts b/src/app/components/libraries/libraries.component.ts index ebe94dbe7..39191fb27 100644 --- a/src/app/components/libraries/libraries.component.ts +++ b/src/app/components/libraries/libraries.component.ts @@ -38,79 +38,78 @@ import { AppExtensionService } from '../../extensions/extension.service'; import { map } from 'rxjs/operators'; @Component({ - templateUrl: './libraries.component.html' + templateUrl: './libraries.component.html' }) export class LibrariesComponent extends PageComponent implements OnInit { + isSmallScreen = false; - isSmallScreen = false; + constructor( + private route: ActivatedRoute, + content: ContentManagementService, + private contentApi: ContentApiService, + store: Store, + extensions: AppExtensionService, + private router: Router, + private breakpointObserver: BreakpointObserver + ) { + super(store, extensions, content); + } - constructor(private route: ActivatedRoute, - content: ContentManagementService, - private contentApi: ContentApiService, - store: Store, - extensions: AppExtensionService, - private router: Router, - private breakpointObserver: BreakpointObserver) { - super(store, extensions, content); + ngOnInit() { + super.ngOnInit(); + + this.subscriptions.push( + this.content.libraryDeleted.subscribe(() => this.reload()), + this.content.libraryCreated.subscribe((node: SiteEntry) => { + this.navigate(node.entry.guid); + }), + + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ); + } + + makeLibraryTooltip(library: any): string { + const { description, title } = library; + + return description || title || ''; + } + + makeLibraryTitle(library: any): string { + const rows = this.documentList.data.getRows(); + const entries = rows.map((r: ShareDataRow) => r.node.entry); + const { title, id } = library; + + let isDuplicate = false; + + if (entries) { + isDuplicate = entries.some((entry: any) => { + return entry.id !== id && entry.title === title; + }); } - ngOnInit() { - super.ngOnInit(); + return isDuplicate ? `${title} (${id})` : `${title}`; + } - this.subscriptions.push( - this.content.libraryDeleted.subscribe(() => this.reload()), - this.content.libraryCreated.subscribe((node: SiteEntry) => { - this.navigate(node.entry.guid); - }), - - this.breakpointObserver - .observe([ - Breakpoints.HandsetPortrait, - Breakpoints.HandsetLandscape - ]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }) - ); + navigateTo(node: SiteEntry) { + if (node && node.entry.guid) { + this.navigate(node.entry.guid); } + } - makeLibraryTooltip(library: any): string { - const { description, title } = library; - - return description || title || ''; - } - - makeLibraryTitle(library: any): string { - const rows = this.documentList.data.getRows(); - const entries = rows.map((r: ShareDataRow) => r.node.entry); - const { title, id } = library; - - let isDuplicate = false; - - if (entries) { - isDuplicate = entries - .some((entry: any) => { - return (entry.id !== id && entry.title === title); - }); - } - - return isDuplicate ? `${title} (${id})` : `${title}`; - } - - navigateTo(node: SiteEntry) { - if (node && node.entry.guid) { - this.navigate(node.entry.guid); - } - } - - navigate(libraryId: string) { - if (libraryId) { - this.contentApi - .getNode(libraryId, { relativePath: '/documentLibrary' }) - .pipe(map(node => node.entry)) - .subscribe(documentLibrary => { - this.router.navigate([ './', documentLibrary.id ], { relativeTo: this.route }); - }); - } + navigate(libraryId: string) { + if (libraryId) { + this.contentApi + .getNode(libraryId, { relativePath: '/documentLibrary' }) + .pipe(map(node => node.entry)) + .subscribe(documentLibrary => { + this.router.navigate(['./', documentLibrary.id], { + relativeTo: this.route + }); + }); } + } } diff --git a/src/app/components/location-link/location-link.component.ts b/src/app/components/location-link/location-link.component.ts index 903ab0d1f..54c6a36c4 100644 --- a/src/app/components/location-link/location-link.component.ts +++ b/src/app/components/location-link/location-link.component.ts @@ -23,7 +23,14 @@ * along with Alfresco. If not, see . */ -import { Component, Input, ChangeDetectionStrategy, OnInit, ViewEncapsulation, HostListener } from '@angular/core'; +import { + Component, + Input, + ChangeDetectionStrategy, + OnInit, + ViewEncapsulation, + HostListener +} from '@angular/core'; import { PathInfo, MinimalNodeEntity } from 'alfresco-js-api'; import { Observable, BehaviorSubject, of } from 'rxjs'; @@ -33,140 +40,148 @@ import { NavigateToParentFolder } from '../../store/actions'; import { ContentApiService } from '../../services/content-api.service'; @Component({ - selector: 'aca-location-link', - template: ` + selector: 'aca-location-link', + template: ` {{ displayText | async }} `, - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, - host: { 'class': 'aca-location-link adf-location-cell' } + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + host: { class: 'aca-location-link adf-location-cell' } }) export class LocationLinkComponent implements OnInit { - private _path: PathInfo; + private _path: PathInfo; - nodeLocation$ = new BehaviorSubject(null); + nodeLocation$ = new BehaviorSubject(null); - @Input() - context: any; + @Input() + context: any; - @Input() - link: any[]; + @Input() + link: any[]; - @Input() - displayText: Observable; + @Input() + displayText: Observable; - @Input() - tooltip: Observable; + @Input() + tooltip: Observable; - @HostListener('mouseenter') onMouseEnter() { - this.getTooltip(this._path); + @HostListener('mouseenter') + onMouseEnter() { + this.getTooltip(this._path); + } + + constructor( + private store: Store, + private contentApi: ContentApiService + ) {} + + goToLocation() { + if (this.context) { + const node: MinimalNodeEntity = this.context.row.node; + this.store.dispatch(new NavigateToParentFolder(node)); } + } - constructor( - private store: Store, - private contentApi: ContentApiService) { - } + ngOnInit() { + if (this.context) { + const node: MinimalNodeEntity = this.context.row.node; + if (node && node.entry && node.entry.path) { + const path = node.entry.path; - goToLocation() { - if (this.context) { - const node: MinimalNodeEntity = this.context.row.node; - this.store.dispatch(new NavigateToParentFolder(node)); + if (path && path.name && path.elements) { + this.displayText = this.getDisplayText(path); + this._path = path; } + } + } + } + + // todo: review once 5.2.3 is out + private getDisplayText(path: PathInfo): Observable { + const elements = path.elements.map(e => e.name); + + // for admin users + if (elements.length === 1 && elements[0] === 'Company Home') { + return of('Personal Files'); } - ngOnInit() { - if (this.context) { - const node: MinimalNodeEntity = this.context.row.node; - if (node && node.entry && node.entry.path) { - const path = node.entry.path; + // for non-admin users + if ( + elements.length === 3 && + elements[0] === 'Company Home' && + elements[1] === 'User Homes' + ) { + return of('Personal Files'); + } - if (path && path.name && path.elements) { - this.displayText = this.getDisplayText(path); - this._path = path; - } + const result = elements[elements.length - 1]; + + if (result === 'documentLibrary') { + const fragment = path.elements[path.elements.length - 2]; + + return new Observable(observer => { + this.contentApi.getNodeInfo(fragment.id).subscribe( + node => { + observer.next( + node.properties['cm:title'] || node.name || fragment.name + ); + observer.complete(); + }, + () => { + observer.next(fragment.name); + observer.complete(); + } + ); + }); + } + + return of(result); + } + + // todo: review once 5.2.3 is out + private getTooltip(path: PathInfo) { + let result: string = null; + + const elements = path.elements.map(e => Object.assign({}, e)); + + if (elements[0].name === 'Company Home') { + elements[0].name = 'Personal Files'; + + if (elements.length > 2) { + if (elements[1].name === 'Sites') { + const fragment = elements[2]; + this.contentApi.getNodeInfo(fragment.id).subscribe( + node => { + elements.splice(0, 2); + elements[0].name = + node.properties['cm:title'] || node.name || fragment.name; + elements.splice(1, 1); + elements.unshift({ id: null, name: 'File Libraries' }); + + result = elements.map(e => e.name).join('/'); + this.nodeLocation$.next(result); + }, + () => { + elements.splice(0, 2); + elements.unshift({ id: null, name: 'File Libraries' }); + elements.splice(2, 1); + + result = elements.map(e => e.name).join('/'); + this.nodeLocation$.next(result); } + ); } + + if (elements[1].name === 'User Homes') { + elements.splice(0, 3); + elements.unshift({ id: null, name: 'Personal Files' }); + } + } } - // todo: review once 5.2.3 is out - private getDisplayText(path: PathInfo): Observable { - const elements = path.elements.map(e => e.name); - - // for admin users - if (elements.length === 1 && elements[0] === 'Company Home') { - return of('Personal Files'); - } - - // for non-admin users - if (elements.length === 3 && elements[0] === 'Company Home' && elements[1] === 'User Homes') { - return of('Personal Files'); - } - - const result = elements[elements.length - 1]; - - if (result === 'documentLibrary') { - const fragment = path.elements[path.elements.length - 2]; - - return new Observable(observer => { - this.contentApi.getNodeInfo(fragment.id).subscribe( - node => { - observer.next(node.properties['cm:title'] || node.name || fragment.name); - observer.complete(); - }, - () => { - observer.next(fragment.name); - observer.complete(); - } - ); - }); - } - - return of(result); - } - - // todo: review once 5.2.3 is out - private getTooltip(path: PathInfo) { - let result: string = null; - - const elements = path.elements.map(e => Object.assign({}, e)); - - if (elements[0].name === 'Company Home') { - elements[0].name = 'Personal Files'; - - if (elements.length > 2) { - if (elements[1].name === 'Sites') { - const fragment = elements[2]; - this.contentApi.getNodeInfo(fragment.id).subscribe( - node => { - elements.splice(0, 2); - elements[0].name = node.properties['cm:title'] || node.name || fragment.name; - elements.splice(1, 1); - elements.unshift({ id: null, name: 'File Libraries' }); - - result = elements.map(e => e.name).join('/'); - this.nodeLocation$.next(result); - }, - () => { - elements.splice(0, 2); - elements.unshift({ id: null, name: 'File Libraries' }); - elements.splice(2, 1); - - result = elements.map(e => e.name).join('/'); - this.nodeLocation$.next(result); - } - ); - } - - if (elements[1].name === 'User Homes') { - elements.splice(0, 3); - elements.unshift({ id: null, name: 'Personal Files'}); - } - } - } - - result = elements.map(e => e.name).join('/'); - this.nodeLocation$.next(result); - } + result = elements.map(e => e.name).join('/'); + this.nodeLocation$.next(result); + } } diff --git a/src/app/components/login/login.component.spec.ts b/src/app/components/login/login.component.spec.ts index 7db3e5a82..1284beb77 100644 --- a/src/app/components/login/login.component.spec.ts +++ b/src/app/components/login/login.component.spec.ts @@ -27,58 +27,57 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router } from '@angular/router'; import { Location } from '@angular/common'; import { TestBed, ComponentFixture } from '@angular/core/testing'; -import { AuthenticationService, UserPreferencesService, AppConfigPipe } from '@alfresco/adf-core'; +import { + AuthenticationService, + UserPreferencesService, + AppConfigPipe +} from '@alfresco/adf-core'; import { LoginComponent } from './login.component'; import { AppTestingModule } from '../../testing/app-testing.module'; describe('LoginComponent', () => { - let fixture: ComponentFixture; - let router: Router; - let userPreference: UserPreferencesService; - let auth: AuthenticationService; - let location: Location; + let fixture: ComponentFixture; + let router: Router; + let userPreference: UserPreferencesService; + let auth: AuthenticationService; + let location: Location; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ AppTestingModule ], - declarations: [ - LoginComponent, - AppConfigPipe - ], - providers: [ - Location - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); - - fixture = TestBed.createComponent(LoginComponent); - - router = TestBed.get(Router); - spyOn(router, 'navigateByUrl'); - - location = TestBed.get(Location); - spyOn(location, 'forward'); - - auth = TestBed.get(AuthenticationService); - spyOn(auth, 'getRedirect').and.returnValue('/some-url'); - - userPreference = TestBed.get(UserPreferencesService); - spyOn(userPreference, 'setStoragePrefix'); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [LoginComponent, AppConfigPipe], + providers: [Location], + schemas: [NO_ERRORS_SCHEMA] }); - describe('OnInit()', () => { - it('should perform normal login when user is not logged in', () => { - spyOn(auth, 'isEcmLoggedIn').and.returnValue(false); - fixture.detectChanges(); + fixture = TestBed.createComponent(LoginComponent); - expect(location.forward).not.toHaveBeenCalled(); - }); + router = TestBed.get(Router); + spyOn(router, 'navigateByUrl'); - it('should redirect when user is logged in', () => { - spyOn(auth, 'isEcmLoggedIn').and.returnValue(true); - fixture.detectChanges(); + location = TestBed.get(Location); + spyOn(location, 'forward'); - expect(location.forward).toHaveBeenCalled(); - }); + auth = TestBed.get(AuthenticationService); + spyOn(auth, 'getRedirect').and.returnValue('/some-url'); + + userPreference = TestBed.get(UserPreferencesService); + spyOn(userPreference, 'setStoragePrefix'); + }); + + describe('OnInit()', () => { + it('should perform normal login when user is not logged in', () => { + spyOn(auth, 'isEcmLoggedIn').and.returnValue(false); + fixture.detectChanges(); + + expect(location.forward).not.toHaveBeenCalled(); }); + + it('should redirect when user is logged in', () => { + spyOn(auth, 'isEcmLoggedIn').and.returnValue(true); + fixture.detectChanges(); + + expect(location.forward).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts index 76aa0f722..20900da2e 100644 --- a/src/app/components/login/login.component.ts +++ b/src/app/components/login/login.component.ts @@ -28,17 +28,17 @@ import { Location } from '@angular/common'; import { AuthenticationService } from '@alfresco/adf-core'; @Component({ - templateUrl: './login.component.html' + templateUrl: './login.component.html' }) export class LoginComponent implements OnInit { - constructor( - private location: Location, - private auth: AuthenticationService, - ) {} + constructor( + private location: Location, + private auth: AuthenticationService + ) {} - ngOnInit() { - if (this.auth.isEcmLoggedIn()) { - this.location.forward(); - } + ngOnInit() { + if (this.auth.isEcmLoggedIn()) { + this.location.forward(); } + } } diff --git a/src/app/components/page.component.spec.ts b/src/app/components/page.component.spec.ts index ee65afb82..6f4027a4e 100644 --- a/src/app/components/page.component.spec.ts +++ b/src/app/components/page.component.spec.ts @@ -26,31 +26,31 @@ import { PageComponent } from './page.component'; class TestClass extends PageComponent { - node: any; + node: any; - constructor() { - super(null, null, null); - } + constructor() { + super(null, null, null); + } } describe('PageComponent', () => { - let component: TestClass; + let component: TestClass; - beforeEach(() => { - component = new TestClass(); + beforeEach(() => { + component = new TestClass(); + }); + + describe('getParentNodeId()', () => { + it('returns parent node id when node is set', () => { + component.node = { id: 'node-id' }; + + expect(component.getParentNodeId()).toBe('node-id'); }); - describe('getParentNodeId()', () => { - it('returns parent node id when node is set', () => { - component.node = { id: 'node-id' }; + it('returns null when node is not set', () => { + component.node = null; - expect(component.getParentNodeId()).toBe('node-id'); - }); - - it('returns null when node is not set', () => { - component.node = null; - - expect(component.getParentNodeId()).toBe(null); - }); + expect(component.getParentNodeId()).toBe(null); }); + }); }); diff --git a/src/app/components/page.component.ts b/src/app/components/page.component.ts index d87bb9663..dfded30db 100644 --- a/src/app/components/page.component.ts +++ b/src/app/components/page.component.ts @@ -23,7 +23,10 @@ * along with Alfresco. If not, see . */ -import { DocumentListComponent, ShareDataRow } from '@alfresco/adf-content-services'; +import { + DocumentListComponent, + ShareDataRow +} from '@alfresco/adf-content-services'; import { ContentActionRef, SelectionState } from '@alfresco/adf-extensions'; import { OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Store } from '@ngrx/store'; @@ -33,97 +36,109 @@ import { takeUntil } from 'rxjs/operators'; import { AppExtensionService } from '../extensions/extension.service'; import { ContentManagementService } from '../services/content-management.service'; import { SetSelectedNodesAction, ViewFileAction } from '../store/actions'; -import { appSelection, currentFolder, documentDisplayMode, infoDrawerOpened, sharedUrl } from '../store/selectors/app.selectors'; +import { + appSelection, + currentFolder, + documentDisplayMode, + infoDrawerOpened, + sharedUrl +} from '../store/selectors/app.selectors'; import { AppStore } from '../store/states/app.state'; export abstract class PageComponent implements OnInit, OnDestroy { + onDestroy$: Subject = new Subject(); - onDestroy$: Subject = new Subject(); + @ViewChild(DocumentListComponent) + documentList: DocumentListComponent; - @ViewChild(DocumentListComponent) - documentList: DocumentListComponent; + title = 'Page'; + infoDrawerOpened$: Observable; + node: MinimalNodeEntryEntity; + selection: SelectionState; + documentDisplayMode$: Observable; + sharedPreviewUrl$: Observable; + actions: Array = []; + viewerToolbarActions: Array = []; + canUpdateNode = false; + canUpload = false; - title = 'Page'; - infoDrawerOpened$: Observable; - node: MinimalNodeEntryEntity; - selection: SelectionState; - documentDisplayMode$: Observable; - sharedPreviewUrl$: Observable; - actions: Array = []; - viewerToolbarActions: Array = []; - canUpdateNode = false; - canUpload = false; + protected subscriptions: Subscription[] = []; - protected subscriptions: Subscription[] = []; + static isLockedNode(node) { + return ( + node.isLocked || + (node.properties && node.properties['cm:lockType'] === 'READ_ONLY_LOCK') + ); + } - static isLockedNode(node) { - return node.isLocked || (node.properties && node.properties['cm:lockType'] === 'READ_ONLY_LOCK'); + constructor( + protected store: Store, + protected extensions: AppExtensionService, + protected content: ContentManagementService + ) {} + + ngOnInit() { + this.sharedPreviewUrl$ = this.store.select(sharedUrl); + this.infoDrawerOpened$ = this.store.select(infoDrawerOpened); + this.documentDisplayMode$ = this.store.select(documentDisplayMode); + + this.store + .select(appSelection) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(selection => { + this.selection = selection; + this.actions = this.extensions.getAllowedToolbarActions(); + this.viewerToolbarActions = this.extensions.getViewerToolbarActions(); + this.canUpdateNode = + this.selection.count === 1 && + this.content.canUpdateNode(selection.first); + }); + + this.store + .select(currentFolder) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(node => { + this.canUpload = node && this.content.canUploadContent(node); + }); + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions = []; + + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + showPreview(node: MinimalNodeEntity) { + if (node && node.entry) { + const parentId = this.node ? this.node.id : null; + this.store.dispatch(new ViewFileAction(node, parentId)); } + } - constructor( - protected store: Store, - protected extensions: AppExtensionService, - protected content: ContentManagementService) {} + getParentNodeId(): string { + return this.node ? this.node.id : null; + } - ngOnInit() { - this.sharedPreviewUrl$ = this.store.select(sharedUrl); - this.infoDrawerOpened$ = this.store.select(infoDrawerOpened); - this.documentDisplayMode$ = this.store.select(documentDisplayMode); + imageResolver(row: ShareDataRow): string | null { + const entry: MinimalNodeEntryEntity = row.node.entry; - this.store - .select(appSelection) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(selection => { - this.selection = selection; - this.actions = this.extensions.getAllowedToolbarActions(); - this.viewerToolbarActions = this.extensions.getViewerToolbarActions(); - this.canUpdateNode = this.selection.count === 1 && this.content.canUpdateNode(selection.first); - }); - - this.store.select(currentFolder) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(node => { - this.canUpload = node && this.content.canUploadContent(node); - }); + if (PageComponent.isLockedNode(entry)) { + return 'assets/images/ic_lock_black_24dp_1x.png'; } + return null; + } - ngOnDestroy() { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); - this.subscriptions = []; - - this.onDestroy$.next(true); - this.onDestroy$.complete(); + reload(): void { + if (this.documentList) { + this.documentList.resetSelection(); + this.store.dispatch(new SetSelectedNodesAction([])); + this.documentList.reload(); } + } - showPreview(node: MinimalNodeEntity) { - if (node && node.entry) { - const parentId = this.node ? this.node.id : null; - this.store.dispatch(new ViewFileAction(node, parentId)); - } - } - - getParentNodeId(): string { - return this.node ? this.node.id : null; - } - - imageResolver(row: ShareDataRow): string | null { - const entry: MinimalNodeEntryEntity = row.node.entry; - - if (PageComponent.isLockedNode(entry)) { - return 'assets/images/ic_lock_black_24dp_1x.png'; - } - return null; - } - - reload(): void { - if (this.documentList) { - this.documentList.resetSelection(); - this.store.dispatch(new SetSelectedNodesAction([])); - this.documentList.reload(); - } - } - - trackByActionId(index: number, action: ContentActionRef) { - return action.id; - } + trackByActionId(index: number, action: ContentActionRef) { + return action.id; + } } diff --git a/src/app/components/permission-manager/permissions-manager.component.theme.scss b/src/app/components/permission-manager/permissions-manager.component.theme.scss index 71ce49ddf..54dbc84dd 100644 --- a/src/app/components/permission-manager/permissions-manager.component.theme.scss +++ b/src/app/components/permission-manager/permissions-manager.component.theme.scss @@ -1,49 +1,49 @@ @mixin aca-permissions-manager-theme($theme) { - $foreground: map-get($theme, foreground); - $accent: map-get($theme, accent); + $foreground: map-get($theme, foreground); + $accent: map-get($theme, accent); - aca-permissions-dialog-panel { - height: 400px; + aca-permissions-dialog-panel { + height: 400px; + } + + .aca-node-permissions-dialog { + .mat-dialog-title { + font-size: 20px; + font-weight: 600; + font-style: normal; + font-stretch: normal; + line-height: 1.6; + margin: 0; + letter-spacing: -0.5px; + color: mat-color($foreground, text, 0.87); } - .aca-node-permissions-dialog { - .mat-dialog-title { - font-size: 20px; - font-weight: 600; - font-style: normal; - font-stretch: normal; - line-height: 1.6; - margin: 0; - letter-spacing: -0.5px; - color: mat-color($foreground, text, 0.87); - } + .mat-dialog-content { + flex: 1 1 auto; + position: relative; + overflow: auto; - .mat-dialog-content { - flex: 1 1 auto; - position: relative; - overflow: auto; - - adf-permission-list { - display: flex; - } - } - - .mat-dialog-actions { - flex: 0 0 auto; - - padding: 8px 8px 24px 8px; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: end; - -ms-flex-pack: end; - justify-content: flex-end; - color: mat-color($foreground, text, 0.54); - - button { - text-transform: uppercase; - font-weight: normal; - } - } + adf-permission-list { + display: flex; + } } + + .mat-dialog-actions { + flex: 0 0 auto; + + padding: 8px 8px 24px 8px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + color: mat-color($foreground, text, 0.54); + + button { + text-transform: uppercase; + font-weight: normal; + } + } + } } diff --git a/src/app/components/permission-manager/permissions-manager.component.ts b/src/app/components/permission-manager/permissions-manager.component.ts index 08265659f..ee237fe03 100644 --- a/src/app/components/permission-manager/permissions-manager.component.ts +++ b/src/app/components/permission-manager/permissions-manager.component.ts @@ -24,7 +24,10 @@ */ import { Component, Input, OnInit, ViewChild } from '@angular/core'; -import { NodePermissionDialogService, PermissionListComponent } from '@alfresco/adf-content-services'; +import { + NodePermissionDialogService, + PermissionListComponent +} from '@alfresco/adf-content-services'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; import { Store } from '@ngrx/store'; import { AppStore } from '../../store/states/app.state'; @@ -34,58 +37,60 @@ import { MatDialog } from '@angular/material'; import { ContentApiService } from '../../services/content-api.service'; @Component({ - selector: 'aca-permissions-manager', - templateUrl: './permissions-manager.component.html' + selector: 'aca-permissions-manager', + templateUrl: './permissions-manager.component.html' }) export class PermissionsManagerComponent implements OnInit { - @ViewChild('permissionList') - permissionList: PermissionListComponent; + @ViewChild('permissionList') + permissionList: PermissionListComponent; - @Input() - nodeId: string; + @Input() + nodeId: string; - toggleStatus = false; + toggleStatus = false; - constructor( - private store: Store, - private dialog: MatDialog, - private contentApi: ContentApiService, - private nodePermissionDialogService: NodePermissionDialogService - ) { - } + constructor( + private store: Store, + private dialog: MatDialog, + private contentApi: ContentApiService, + private nodePermissionDialogService: NodePermissionDialogService + ) {} - ngOnInit() { - this.contentApi.getNodeInfo(this.nodeId, {include: ['permissions'] }).subscribe( (currentNode: MinimalNodeEntryEntity) => { - this.toggleStatus = currentNode.permissions.isInheritanceEnabled; - }); - } + ngOnInit() { + this.contentApi + .getNodeInfo(this.nodeId, { include: ['permissions'] }) + .subscribe((currentNode: MinimalNodeEntryEntity) => { + this.toggleStatus = currentNode.permissions.isInheritanceEnabled; + }); + } - onError(errorMessage: string) { - this.store.dispatch(new SnackbarErrorAction(errorMessage)); - } + onError(errorMessage: string) { + this.store.dispatch(new SnackbarErrorAction(errorMessage)); + } - onUpdate(event) { - this.permissionList.reload(); - } + onUpdate(event) { + this.permissionList.reload(); + } - onUpdatedPermissions(node: MinimalNodeEntryEntity) { - this.toggleStatus = node.permissions.isInheritanceEnabled; - this.permissionList.reload(); - } + onUpdatedPermissions(node: MinimalNodeEntryEntity) { + this.toggleStatus = node.permissions.isInheritanceEnabled; + this.permissionList.reload(); + } - openAddPermissionDialog(event: Event) { - this.nodePermissionDialogService.updateNodePermissionByDialog(this.nodeId) - .subscribe(() => { - this.dialog.open(NodePermissionsDialogComponent, { - data: { nodeId: this.nodeId }, - panelClass: 'aca-permissions-dialog-panel', - width: '800px' - } - ); - }, - (error) => { - this.store.dispatch(new SnackbarErrorAction(error)); - } - ); - } + openAddPermissionDialog(event: Event) { + this.nodePermissionDialogService + .updateNodePermissionByDialog(this.nodeId) + .subscribe( + () => { + this.dialog.open(NodePermissionsDialogComponent, { + data: { nodeId: this.nodeId }, + panelClass: 'aca-permissions-dialog-panel', + width: '800px' + }); + }, + error => { + this.store.dispatch(new SnackbarErrorAction(error)); + } + ); + } } diff --git a/src/app/components/preview/preview-extension.component.ts b/src/app/components/preview/preview-extension.component.ts index a1c2201c9..e4a72df48 100644 --- a/src/app/components/preview/preview-extension.component.ts +++ b/src/app/components/preview/preview-extension.component.ts @@ -24,82 +24,82 @@ */ import { - Component, - Input, - ComponentRef, - OnInit, - ComponentFactoryResolver, - ViewChild, - ViewContainerRef, - OnDestroy, - OnChanges + Component, + Input, + ComponentRef, + OnInit, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, + OnDestroy, + OnChanges } from '@angular/core'; import { AppExtensionService } from '../../extensions/extension.service'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; @Component({ - selector: 'app-preview-extension', - template: `
` + selector: 'app-preview-extension', + template: `
` }) export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { - @ViewChild('content', { read: ViewContainerRef }) - content: ViewContainerRef; + @ViewChild('content', { read: ViewContainerRef }) + content: ViewContainerRef; - @Input() - id: string; + @Input() + id: string; - @Input() - url: string; + @Input() + url: string; - @Input() - extension: string; + @Input() + extension: string; - @Input() - node: MinimalNodeEntryEntity; + @Input() + node: MinimalNodeEntryEntity; - private componentRef: ComponentRef; + private componentRef: ComponentRef; - constructor( - private extensions: AppExtensionService, - private componentFactoryResolver: ComponentFactoryResolver - ) {} + constructor( + private extensions: AppExtensionService, + private componentFactoryResolver: ComponentFactoryResolver + ) {} - ngOnInit() { - if (!this.id) { - return; - } - - const componentType = this.extensions.getComponentById(this.id); - if (componentType) { - const factory = this.componentFactoryResolver.resolveComponentFactory( - componentType - ); - if (factory) { - this.content.clear(); - this.componentRef = this.content.createComponent(factory, 0); - this.updateInstance(); - } - } + ngOnInit() { + if (!this.id) { + return; } - ngOnChanges() { + const componentType = this.extensions.getComponentById(this.id); + if (componentType) { + const factory = this.componentFactoryResolver.resolveComponentFactory( + componentType + ); + if (factory) { + this.content.clear(); + this.componentRef = this.content.createComponent(factory, 0); this.updateInstance(); + } } + } - ngOnDestroy() { - if (this.componentRef) { - this.componentRef.destroy(); - this.componentRef = null; - } + ngOnChanges() { + this.updateInstance(); + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + this.componentRef = null; } + } - private updateInstance() { - if (this.componentRef && this.componentRef.instance) { - const instance = this.componentRef.instance; + private updateInstance() { + if (this.componentRef && this.componentRef.instance) { + const instance = this.componentRef.instance; - instance.node = this.node; - instance.url = this.url; - instance.extension = this.extension; - } + instance.node = this.node; + instance.url = this.url; + instance.extension = this.extension; } + } } diff --git a/src/app/components/preview/preview.component.scss b/src/app/components/preview/preview.component.scss index 6a2cae035..714aad589 100644 --- a/src/app/components/preview/preview.component.scss +++ b/src/app/components/preview/preview.component.scss @@ -1,4 +1,4 @@ .app-preview { - width: 100%; - height: 100%; + width: 100%; + height: 100%; } diff --git a/src/app/components/preview/preview.component.spec.ts b/src/app/components/preview/preview.component.spec.ts index cdd826c16..6c457a935 100644 --- a/src/app/components/preview/preview.component.spec.ts +++ b/src/app/components/preview/preview.component.spec.ts @@ -26,7 +26,11 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { TestBed, ComponentFixture } from '@angular/core/testing'; -import { UserPreferencesService, AppConfigPipe, NodeFavoriteDirective } from '@alfresco/adf-core'; +import { + UserPreferencesService, + AppConfigPipe, + NodeFavoriteDirective +} from '@alfresco/adf-core'; import { PreviewComponent } from './preview.component'; import { of, throwError } from 'rxjs'; import { EffectsModule } from '@ngrx/effects'; @@ -36,624 +40,661 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { ContentApiService } from '../../services/content-api.service'; describe('PreviewComponent', () => { + let fixture: ComponentFixture; + let component: PreviewComponent; + let router: Router; + let route: ActivatedRoute; + let preferences: UserPreferencesService; + let contentApi: ContentApiService; - let fixture: ComponentFixture; - let component: PreviewComponent; - let router: Router; - let route: ActivatedRoute; - let preferences: UserPreferencesService; - let contentApi: ContentApiService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - AppTestingModule, - EffectsModule.forRoot([NodeEffects]) - ], - declarations: [ - AppConfigPipe, - PreviewComponent, - NodeFavoriteDirective, - ExperimentalDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); - - fixture = TestBed.createComponent(PreviewComponent); - component = fixture.componentInstance; - - router = TestBed.get(Router); - route = TestBed.get(ActivatedRoute); - preferences = TestBed.get(UserPreferencesService); - contentApi = TestBed.get(ContentApiService); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule, EffectsModule.forRoot([NodeEffects])], + declarations: [ + AppConfigPipe, + PreviewComponent, + NodeFavoriteDirective, + ExperimentalDirective + ], + schemas: [NO_ERRORS_SCHEMA] }); - it('should extract the property path root', () => { - expect(component.getRootField('some.property.path')).toBe('some'); - expect(component.getRootField('some')).toBe('some'); - expect(component.getRootField('')).toBe(''); - expect(component.getRootField(null)).toBe(null); + fixture = TestBed.createComponent(PreviewComponent); + component = fixture.componentInstance; + + router = TestBed.get(Router); + route = TestBed.get(ActivatedRoute); + preferences = TestBed.get(UserPreferencesService); + contentApi = TestBed.get(ContentApiService); + }); + + it('should extract the property path root', () => { + expect(component.getRootField('some.property.path')).toBe('some'); + expect(component.getRootField('some')).toBe('some'); + expect(component.getRootField('')).toBe(''); + expect(component.getRootField(null)).toBe(null); + }); + + it('should navigate to previous node in sub-folder', () => { + spyOn(router, 'navigate').and.stub(); + + component.previewLocation = 'personal-files'; + component.folderId = 'folder1'; + component.previousNodeId = 'previous1'; + component.onNavigateBefore(); + + expect(router.navigate).toHaveBeenCalledWith([ + 'personal-files', + 'folder1', + 'preview', + 'previous1' + ]); + }); + + it('should navigate back to previous node in the root path', () => { + spyOn(router, 'navigate').and.stub(); + + component.previewLocation = 'personal-files'; + component.folderId = null; + component.previousNodeId = 'previous1'; + component.onNavigateBefore(); + + expect(router.navigate).toHaveBeenCalledWith([ + 'personal-files', + 'preview', + 'previous1' + ]); + }); + + it('should not navigate back if node unset', () => { + spyOn(router, 'navigate').and.stub(); + + component.previousNodeId = null; + component.onNavigateBefore(); + + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should navigate to next node in sub-folder', () => { + spyOn(router, 'navigate').and.stub(); + + component.previewLocation = 'personal-files'; + component.folderId = 'folder1'; + component.nextNodeId = 'next1'; + component.onNavigateNext(); + + expect(router.navigate).toHaveBeenCalledWith([ + 'personal-files', + 'folder1', + 'preview', + 'next1' + ]); + }); + + it('should navigate to next node in the root path', () => { + spyOn(router, 'navigate').and.stub(); + + component.previewLocation = 'personal-files'; + component.folderId = null; + component.nextNodeId = 'next1'; + component.onNavigateNext(); + + expect(router.navigate).toHaveBeenCalledWith([ + 'personal-files', + 'preview', + 'next1' + ]); + }); + + it('should not navigate back if node unset', () => { + spyOn(router, 'navigate').and.stub(); + + component.nextNodeId = null; + component.onNavigateNext(); + + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should generate preview path for a folder only', () => { + component.previewLocation = 'personal-files'; + + expect(component.getPreviewPath('folder1', null)).toEqual([ + 'personal-files', + 'folder1' + ]); + }); + + it('should generate preview path for a folder and a node', () => { + component.previewLocation = 'personal-files'; + + expect(component.getPreviewPath('folder1', 'node1')).toEqual([ + 'personal-files', + 'folder1', + 'preview', + 'node1' + ]); + }); + + it('should generate preview path for a node only', () => { + component.previewLocation = 'personal-files'; + + expect(component.getPreviewPath(null, 'node1')).toEqual([ + 'personal-files', + 'preview', + 'node1' + ]); + }); + + it('should generate preview for the location only', () => { + component.previewLocation = 'personal-files'; + + expect(component.getPreviewPath(null, null)).toEqual(['personal-files']); + }); + + it('should navigate back to root path upon close', () => { + spyOn(router, 'navigate').and.stub(); + + component.routesSkipNavigation = []; + component.previewLocation = 'libraries'; + component.folderId = null; + + component.onVisibilityChanged(false); + + expect(router.navigate).toHaveBeenCalledWith(['libraries', {}]); + }); + + it('should navigate back to folder path upon close', () => { + spyOn(router, 'navigate').and.stub(); + + component.routesSkipNavigation = []; + component.previewLocation = 'libraries'; + component.folderId = 'site1'; + + component.onVisibilityChanged(false); + + expect(router.navigate).toHaveBeenCalledWith(['libraries', {}, 'site1']); + }); + + it('should not navigate to root path for certain routes upon close', () => { + spyOn(router, 'navigate').and.stub(); + + component.routesSkipNavigation = ['shared']; + component.previewLocation = 'shared'; + component.folderId = 'folder1'; + + component.onVisibilityChanged(false); + + expect(router.navigate).toHaveBeenCalledWith(['shared', {}]); + }); + + it('should not navigate back if viewer is still visible', () => { + spyOn(router, 'navigate').and.stub(); + + component.routesSkipNavigation = []; + component.previewLocation = 'shared'; + + component.onVisibilityChanged(true); + + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should enable multiple document navigation from route data', () => { + route.snapshot.data = { + navigateMultiple: true + }; + + component.ngOnInit(); + + expect(component.navigateMultiple).toBeTruthy(); + }); + + it('should not enable multiple document navigation from route data', () => { + route.snapshot.data = {}; + + component.ngOnInit(); + + expect(component.navigateMultiple).toBeFalsy(); + }); + + it('should fetch navigation source from route', () => { + route.snapshot.data = { + navigateSource: 'personal-files' + }; + + component.ngOnInit(); + + expect(component.navigateSource).toBe('personal-files'); + }); + + it('should fetch case-insensitive source from route', () => { + route.snapshot.data = { + navigateSource: 'PERSONAL-FILES' + }; + + component.navigationSources = ['personal-files']; + component.ngOnInit(); + + expect(component.navigateSource).toBe('PERSONAL-FILES'); + }); + + it('should fetch only permitted navigation source from route', () => { + route.snapshot.data = { + navigateSource: 'personal-files' + }; + + component.navigationSources = ['shared']; + component.ngOnInit(); + + expect(component.navigateSource).toBeNull(); + }); + + it('should display document upon init', () => { + route.params = of({ + folderId: 'folder1', + nodeId: 'node1' }); - it('should navigate to previous node in sub-folder', () => { - spyOn(router, 'navigate').and.stub(); + spyOn(component, 'displayNode').and.stub(); - component.previewLocation = 'personal-files'; - component.folderId = 'folder1'; - component.previousNodeId = 'previous1'; - component.onNavigateBefore(); + component.ngOnInit(); - expect(router.navigate).toHaveBeenCalledWith( - ['personal-files', 'folder1', 'preview', 'previous1'] - ); - }); + expect(component.folderId).toBe('folder1'); + expect(component.displayNode).toHaveBeenCalledWith('node1'); + }); - it('should navigate back to previous node in the root path', () => { - spyOn(router, 'navigate').and.stub(); + it('should return empty nearest nodes for missing node id', async () => { + const nearest = await component.getNearestNodes(null, 'folder1'); - component.previewLocation = 'personal-files'; - component.folderId = null; - component.previousNodeId = 'previous1'; - component.onNavigateBefore(); + expect(nearest).toEqual({ left: null, right: null }); + }); - expect(router.navigate).toHaveBeenCalledWith( - ['personal-files', 'preview', 'previous1'] - ); - }); + it('should return empty nearest nodes for missing folder id', async () => { + const nearest = await component.getNearestNodes('node1', null); - it('should not navigate back if node unset', () => { - spyOn(router, 'navigate').and.stub(); + expect(nearest).toEqual({ left: null, right: null }); + }); - component.previousNodeId = null; - component.onNavigateBefore(); + it('should return empty nearest nodes for crashed fields id request', async () => { + spyOn(component, 'getFileIds').and.returnValue(Promise.reject('err')); - expect(router.navigate).not.toHaveBeenCalled(); - }); + const nearest = await component.getNearestNodes('node1', 'folder1'); - it('should navigate to next node in sub-folder', () => { - spyOn(router, 'navigate').and.stub(); + expect(nearest).toEqual({ left: null, right: null }); + }); - component.previewLocation = 'personal-files'; - component.folderId = 'folder1'; - component.nextNodeId = 'next1'; - component.onNavigateNext(); + it('should return nearest nodes', async () => { + spyOn(component, 'getFileIds').and.returnValue( + Promise.resolve(['node1', 'node2', 'node3', 'node4', 'node5']) + ); - expect(router.navigate).toHaveBeenCalledWith( - ['personal-files', 'folder1', 'preview', 'next1'] - ); - }); + let nearest = await component.getNearestNodes('node1', 'folder1'); + expect(nearest).toEqual({ left: null, right: 'node2' }); - it('should navigate to next node in the root path', () => { - spyOn(router, 'navigate').and.stub(); + nearest = await component.getNearestNodes('node3', 'folder1'); + expect(nearest).toEqual({ left: 'node2', right: 'node4' }); - component.previewLocation = 'personal-files'; - component.folderId = null; - component.nextNodeId = 'next1'; - component.onNavigateNext(); + nearest = await component.getNearestNodes('node5', 'folder1'); + expect(nearest).toEqual({ left: 'node4', right: null }); + }); - expect(router.navigate).toHaveBeenCalledWith( - ['personal-files', 'preview', 'next1'] - ); - }); + it('should return empty nearest nodes if node is already deleted', async () => { + spyOn(component, 'getFileIds').and.returnValue( + Promise.resolve(['node1', 'node2', 'node3', 'node4', 'node5']) + ); - it('should not navigate back if node unset', () => { - spyOn(router, 'navigate').and.stub(); + const nearest = await component.getNearestNodes('node9', 'folder1'); + expect(nearest).toEqual({ left: null, right: null }); + }); - component.nextNodeId = null; - component.onNavigateNext(); + it('should not display node when id is missing', async () => { + spyOn(router, 'navigate').and.stub(); + spyOn(contentApi, 'getNodeInfo').and.returnValue(of(null)); - expect(router.navigate).not.toHaveBeenCalled(); - }); + await component.displayNode(null); - it('should generate preview path for a folder only', () => { - component.previewLocation = 'personal-files'; + expect(contentApi.getNodeInfo).not.toHaveBeenCalled(); + expect(router.navigate).not.toHaveBeenCalled(); + }); - expect(component.getPreviewPath('folder1', null)).toEqual( - ['personal-files', 'folder1'] - ); - }); + it('should navigate to original location if node not found', async () => { + spyOn(router, 'navigate').and.stub(); + spyOn(contentApi, 'getNodeInfo').and.returnValue(of(null)); - it('should generate preview path for a folder and a node', () => { - component.previewLocation = 'personal-files'; + component.previewLocation = 'personal-files'; + await component.displayNode('folder1'); - expect(component.getPreviewPath('folder1', 'node1')).toEqual( - ['personal-files', 'folder1', 'preview', 'node1'] - ); - }); + expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); + }); - it('should generate preview path for a node only', () => { - component.previewLocation = 'personal-files'; + it('should navigate to original location if node is not a File', async () => { + spyOn(router, 'navigate').and.stub(); + spyOn(contentApi, 'getNodeInfo').and.returnValue( + of({ + isFile: false + }) + ); - expect(component.getPreviewPath(null, 'node1')).toEqual( - ['personal-files', 'preview', 'node1'] - ); - }); + component.previewLocation = 'personal-files'; + await component.displayNode('folder1'); - it('should generate preview for the location only', () => { - component.previewLocation = 'personal-files'; + expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); + }); - expect(component.getPreviewPath(null, null)).toEqual( - ['personal-files'] - ); - }); + it('should navigate to original location in case of Alfresco API errors', async () => { + spyOn(router, 'navigate').and.stub(); + spyOn(contentApi, 'getNodeInfo').and.returnValue(throwError('error')); - it('should navigate back to root path upon close', () => { - spyOn(router, 'navigate').and.stub(); + component.previewLocation = 'personal-files'; + await component.displayNode('folder1'); - component.routesSkipNavigation = []; - component.previewLocation = 'libraries'; - component.folderId = null; + expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); + }); - component.onVisibilityChanged(false); + // todo: Fix after Angular6 migration + xit('should navigate to original location in case of internal errors', async () => { + spyOn(router, 'navigate').and.stub(); + spyOn(contentApi, 'getNodeInfo').and.returnValue( + of({ + isFile: true + }) + ); + spyOn(component, 'getNearestNodes').and.returnValue( + Promise.reject('error') + ); - expect(router.navigate).toHaveBeenCalledWith( - ['libraries', {}] - ); - }); + component.previewLocation = 'personal-files'; + await component.displayNode('folder1'); - it('should navigate back to folder path upon close', () => { - spyOn(router, 'navigate').and.stub(); - - component.routesSkipNavigation = []; - component.previewLocation = 'libraries'; - component.folderId = 'site1'; - - component.onVisibilityChanged(false); - - expect(router.navigate).toHaveBeenCalledWith( - ['libraries', {}, 'site1'] - ); - }); - - it('should not navigate to root path for certain routes upon close', () => { - spyOn(router, 'navigate').and.stub(); - - component.routesSkipNavigation = ['shared']; - component.previewLocation = 'shared'; - component.folderId = 'folder1'; - - component.onVisibilityChanged(false); - - expect(router.navigate).toHaveBeenCalledWith( - ['shared', {}] - ); - }); - - it('should not navigate back if viewer is still visible', () => { - spyOn(router, 'navigate').and.stub(); - - component.routesSkipNavigation = []; - component.previewLocation = 'shared'; - - component.onVisibilityChanged(true); - - expect(router.navigate).not.toHaveBeenCalled(); - }); - - it('should enable multiple document navigation from route data', () => { - route.snapshot.data = { - navigateMultiple: true - }; - - component.ngOnInit(); - - expect(component.navigateMultiple).toBeTruthy(); - }); - - it('should not enable multiple document navigation from route data', () => { - route.snapshot.data = {}; - - component.ngOnInit(); - - expect(component.navigateMultiple).toBeFalsy(); - }); - - it('should fetch navigation source from route', () => { - route.snapshot.data = { - navigateSource: 'personal-files' - }; - - component.ngOnInit(); - - expect(component.navigateSource).toBe('personal-files'); - }); - - it('should fetch case-insensitive source from route', () => { - route.snapshot.data = { - navigateSource: 'PERSONAL-FILES' - }; - - component.navigationSources = ['personal-files']; - component.ngOnInit(); - - expect(component.navigateSource).toBe('PERSONAL-FILES'); - }); - - it('should fetch only permitted navigation source from route', () => { - route.snapshot.data = { - navigateSource: 'personal-files' - }; - - component.navigationSources = ['shared']; - component.ngOnInit(); - - expect(component.navigateSource).toBeNull(); - }); - - it('should display document upon init', () => { - route.params = of({ - folderId: 'folder1', - nodeId: 'node1' - }); - - spyOn(component, 'displayNode').and.stub(); - - component.ngOnInit(); - - expect(component.folderId).toBe('folder1'); - expect(component.displayNode).toHaveBeenCalledWith('node1'); - }); - - it('should return empty nearest nodes for missing node id', async () => { - const nearest = await component.getNearestNodes(null, 'folder1'); - - expect(nearest).toEqual({ left: null, right: null }); - }); - - it('should return empty nearest nodes for missing folder id', async () => { - const nearest = await component.getNearestNodes('node1', null); - - expect(nearest).toEqual({ left: null, right: null }); - }); - - it('should return empty nearest nodes for crashed fields id request', async () => { - spyOn(component, 'getFileIds').and.returnValue(Promise.reject('err')); - - const nearest = await component.getNearestNodes('node1', 'folder1'); - - expect(nearest).toEqual({ left: null, right: null }); - }); - - it('should return nearest nodes', async () => { - spyOn(component, 'getFileIds').and.returnValue(Promise.resolve( - [ 'node1', 'node2', 'node3', 'node4', 'node5' ] - )); - - let nearest = await component.getNearestNodes('node1', 'folder1'); - expect(nearest).toEqual({ left: null, right: 'node2' }); - - nearest = await component.getNearestNodes('node3', 'folder1'); - expect(nearest).toEqual({ left: 'node2', right: 'node4' }); - - nearest = await component.getNearestNodes('node5', 'folder1'); - expect(nearest).toEqual({ left: 'node4', right: null }); - }); - - it('should return empty nearest nodes if node is already deleted', async () => { - spyOn(component, 'getFileIds').and.returnValue(Promise.resolve( - [ 'node1', 'node2', 'node3', 'node4', 'node5' ] - )); - - const nearest = await component.getNearestNodes('node9', 'folder1'); - expect(nearest).toEqual({ left: null, right: null }); - }); - - it('should not display node when id is missing', async () => { - spyOn(router, 'navigate').and.stub(); - spyOn(contentApi, 'getNodeInfo').and.returnValue( - of(null) - ); - - await component.displayNode(null); - - expect(contentApi.getNodeInfo).not.toHaveBeenCalled(); - expect(router.navigate).not.toHaveBeenCalled(); - }); - - it('should navigate to original location if node not found', async () => { - spyOn(router, 'navigate').and.stub(); - spyOn(contentApi, 'getNodeInfo').and.returnValue( - of(null) - ); - - component.previewLocation = 'personal-files'; - await component.displayNode('folder1'); - - expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); - expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); - }); - - it('should navigate to original location if node is not a File', async () => { - spyOn(router, 'navigate').and.stub(); - spyOn(contentApi, 'getNodeInfo').and.returnValue( - of({ - isFile: false - }) - ); - - component.previewLocation = 'personal-files'; - await component.displayNode('folder1'); - - expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); - expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); - }); - - it('should navigate to original location in case of Alfresco API errors', async () => { - spyOn(router, 'navigate').and.stub(); - spyOn(contentApi, 'getNodeInfo').and.returnValue( - throwError('error') - ); - - component.previewLocation = 'personal-files'; - await component.displayNode('folder1'); - - expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); - expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); - }); - - // todo: Fix after Angular6 migration - xit('should navigate to original location in case of internal errors', async () => { - spyOn(router, 'navigate').and.stub(); - spyOn(contentApi, 'getNodeInfo').and.returnValue( - of({ - isFile: true - }) - ); - spyOn(component, 'getNearestNodes').and.returnValue( - Promise.reject('error') - ); - - component.previewLocation = 'personal-files'; - await component.displayNode('folder1'); - - expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); - expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); - }); - - it('should setup node for displaying', async () => { - spyOn(router, 'navigate').and.stub(); - spyOn(component, 'getNearestNodes').and.returnValue({ left: 'node1', right: 'node3' }); - spyOn(contentApi, 'getNodeInfo').and.returnValue( - of({ - id: 'node2', - parentId: 'parent1', - isFile: true - }) - ); - - await component.displayNode('node2'); - - expect(component.previousNodeId).toBe('node1'); - expect(component.nextNodeId).toBe('node3'); - expect(component.nodeId).toBe('node2'); - expect(router.navigate).not.toHaveBeenCalled(); - }); - - it('should fetch and sort file ids for personal-files', async () => { - preferences.set('personal-files.sorting.key', 'name'); - preferences.set('personal-files.sorting.direction', 'desc'); - - spyOn(contentApi, 'getNodeChildren').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node1', name: 'node 1' } }, - { entry: { id: 'node2', name: 'node 2' } } - ] - } - }) - ); - - const ids = await component.getFileIds('personal-files', 'folder1'); - expect(ids).toEqual(['node2', 'node1']); - }); - - it('should fetch file ids for personal-files with default sorting for missing key', async () => { - preferences.set('personal-files.sorting.key', 'missing'); - preferences.set('personal-files.sorting.direction', 'desc'); - - spyOn(contentApi, 'getNodeChildren').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node1', name: 'node 1' } }, - { entry: { id: 'node2', name: 'node 2' } } - ] - } - }) - ); - - const ids = await component.getFileIds('personal-files', 'folder1'); - expect(ids).toEqual(['node1', 'node2']); - }); - - it('should require folder id to fetch ids for personal-files', async () => { - const ids = await component.getFileIds('personal-files', null); - expect(ids).toEqual([]); - }); - - it('should sort file ids for personal-files with [modifiedAt desc]', async () => { - spyOn(preferences, 'get').and.returnValue(null); - - spyOn(contentApi, 'getNodeChildren').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node1', name: 'node 1', modifiedAt: 1 } }, - { entry: { id: 'node2', name: 'node 2', modifiedAt: 2 } } - ] - } - }) - ); - - const ids = await component.getFileIds('personal-files', 'folder1'); - expect(ids).toEqual(['node2', 'node1']); - }); - - it('should fetch and sort file ids for libraries', async () => { - preferences.set('personal-files.sorting.key', 'name'); - preferences.set('personal-files.sorting.direction', 'desc'); - - spyOn(contentApi, 'getNodeChildren').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node1', name: 'node 1' } }, - { entry: { id: 'node2', name: 'node 2' } } - ] - } - }) - ); - - const ids = await component.getFileIds('libraries', 'site1'); - expect(ids).toEqual(['node2', 'node1']); - }); - - it('should require folder id to fetch ids for libraries', async () => { - const ids = await component.getFileIds('libraries', null); - expect(ids).toEqual([]); - }); - - it('should sort file ids for libraries with [modifiedAt desc]', async () => { - spyOn(preferences, 'get').and.returnValue(null); - - spyOn(contentApi, 'getNodeChildren').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } }, - { entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } } - ] - } - }) - ); - - const ids = await component.getFileIds('libraries', 'folder1'); - expect(ids).toEqual(['node2', 'node1']); - }); - - it('should fetch and sort ids for favorites', async () => { - preferences.set('favorites.sorting.key', 'name'); - preferences.set('favorites.sorting.direction', 'desc'); - - spyOn(contentApi, 'getFavorites').and.returnValue( - of({ - list: { - entries: [ - { entry: { target: { file: { id: 'file3', name: 'file 3' } } } }, - { entry: { target: { file: { id: 'file1', name: 'file 1' } } } }, - { entry: { target: { file: { id: 'file2', name: 'file 2' } } } } - ] - } - }) - ); - - const ids = await component.getFileIds('favorites'); - expect(ids).toEqual(['file3', 'file2', 'file1']); - }); - - it('should sort file ids for favorites with [modifiedAt desc]', async () => { - spyOn(preferences, 'get').and.returnValue(null); - - spyOn(contentApi, 'getFavorites').and.returnValue( - of({ - list: { - entries: [ - { entry: { target: { file: { id: 'file3', modifiedAt: new Date(3) } } } }, - { entry: { target: { file: { id: 'file1', modifiedAt: new Date(1) } } } }, - { entry: { target: { file: { id: 'file2', modifiedAt: new Date(2) } } } } - ] - } - }) - ); - - const ids = await component.getFileIds('favorites'); - expect(ids).toEqual(['file3', 'file2', 'file1']); - }); - - it('should fetch and sort file ids for shared files', async () => { - preferences.set('shared.sorting.key', 'name'); - preferences.set('shared.sorting.direction', 'asc'); - - spyOn(contentApi, 'findSharedLinks').and.returnValue( - of({ - list: { - entries: [ - { entry: { nodeId: 'node2', name: 'node 2', modifiedAt: new Date(2) } }, - { entry: { nodeId: 'node1', name: 'node 1', modifiedAt: new Date(1) } } - ] - } - }) - ); - - const ids = await component.getFileIds('shared'); - expect(ids).toEqual(['node1', 'node2']); - }); - - it('should sort file ids for favorites with [modifiedAt desc]', async () => { - spyOn(preferences, 'get').and.returnValue(null); - - spyOn(contentApi, 'findSharedLinks').and.returnValue( - of({ - list: { - entries: [ - { entry: { nodeId: 'node2', name: 'node 2', modifiedAt: new Date(2) } }, - { entry: { nodeId: 'node1', name: 'node 1', modifiedAt: new Date(1) } } - ] - } - }) - ); - - const ids = await component.getFileIds('shared'); - expect(ids).toEqual(['node2', 'node1']); - }); - - it('should fetch and sort ids for recent-files', async () => { - preferences.set('recent-files.sorting.key', 'name'); - preferences.set('recent-files.sorting.direction', 'asc'); - - spyOn(contentApi, 'getPerson').and.returnValue( - of({ - entry: { id: 'user' } - }) - ); - - spyOn(contentApi, 'search').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } }, - { entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } } - ] - } - }) - ); - - const ids = await component.getFileIds('recent-files'); - expect(ids).toEqual(['node1', 'node2']); - }); - - it('should sort file ids for favorites with [modifiedAt desc]', async () => { - spyOn(preferences, 'get').and.returnValue(null); - - spyOn(contentApi, 'getPerson').and.returnValue( - of({ - entry: { id: 'user' } - }) - ); - - spyOn(contentApi, 'search').and.returnValue( - of({ - list: { - entries: [ - { entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } }, - { entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } } - ] - } - }) - ); - - const ids = await component.getFileIds('recent-files'); - expect(ids).toEqual(['node2', 'node1']); + expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); + }); + + it('should setup node for displaying', async () => { + spyOn(router, 'navigate').and.stub(); + spyOn(component, 'getNearestNodes').and.returnValue({ + left: 'node1', + right: 'node3' }); + spyOn(contentApi, 'getNodeInfo').and.returnValue( + of({ + id: 'node2', + parentId: 'parent1', + isFile: true + }) + ); + + await component.displayNode('node2'); + + expect(component.previousNodeId).toBe('node1'); + expect(component.nextNodeId).toBe('node3'); + expect(component.nodeId).toBe('node2'); + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should fetch and sort file ids for personal-files', async () => { + preferences.set('personal-files.sorting.key', 'name'); + preferences.set('personal-files.sorting.direction', 'desc'); + + spyOn(contentApi, 'getNodeChildren').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node1', name: 'node 1' } }, + { entry: { id: 'node2', name: 'node 2' } } + ] + } + }) + ); + + const ids = await component.getFileIds('personal-files', 'folder1'); + expect(ids).toEqual(['node2', 'node1']); + }); + + it('should fetch file ids for personal-files with default sorting for missing key', async () => { + preferences.set('personal-files.sorting.key', 'missing'); + preferences.set('personal-files.sorting.direction', 'desc'); + + spyOn(contentApi, 'getNodeChildren').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node1', name: 'node 1' } }, + { entry: { id: 'node2', name: 'node 2' } } + ] + } + }) + ); + + const ids = await component.getFileIds('personal-files', 'folder1'); + expect(ids).toEqual(['node1', 'node2']); + }); + + it('should require folder id to fetch ids for personal-files', async () => { + const ids = await component.getFileIds('personal-files', null); + expect(ids).toEqual([]); + }); + + it('should sort file ids for personal-files with [modifiedAt desc]', async () => { + spyOn(preferences, 'get').and.returnValue(null); + + spyOn(contentApi, 'getNodeChildren').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node1', name: 'node 1', modifiedAt: 1 } }, + { entry: { id: 'node2', name: 'node 2', modifiedAt: 2 } } + ] + } + }) + ); + + const ids = await component.getFileIds('personal-files', 'folder1'); + expect(ids).toEqual(['node2', 'node1']); + }); + + it('should fetch and sort file ids for libraries', async () => { + preferences.set('personal-files.sorting.key', 'name'); + preferences.set('personal-files.sorting.direction', 'desc'); + + spyOn(contentApi, 'getNodeChildren').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node1', name: 'node 1' } }, + { entry: { id: 'node2', name: 'node 2' } } + ] + } + }) + ); + + const ids = await component.getFileIds('libraries', 'site1'); + expect(ids).toEqual(['node2', 'node1']); + }); + + it('should require folder id to fetch ids for libraries', async () => { + const ids = await component.getFileIds('libraries', null); + expect(ids).toEqual([]); + }); + + it('should sort file ids for libraries with [modifiedAt desc]', async () => { + spyOn(preferences, 'get').and.returnValue(null); + + spyOn(contentApi, 'getNodeChildren').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } }, + { entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } } + ] + } + }) + ); + + const ids = await component.getFileIds('libraries', 'folder1'); + expect(ids).toEqual(['node2', 'node1']); + }); + + it('should fetch and sort ids for favorites', async () => { + preferences.set('favorites.sorting.key', 'name'); + preferences.set('favorites.sorting.direction', 'desc'); + + spyOn(contentApi, 'getFavorites').and.returnValue( + of({ + list: { + entries: [ + { entry: { target: { file: { id: 'file3', name: 'file 3' } } } }, + { entry: { target: { file: { id: 'file1', name: 'file 1' } } } }, + { entry: { target: { file: { id: 'file2', name: 'file 2' } } } } + ] + } + }) + ); + + const ids = await component.getFileIds('favorites'); + expect(ids).toEqual(['file3', 'file2', 'file1']); + }); + + it('should sort file ids for favorites with [modifiedAt desc]', async () => { + spyOn(preferences, 'get').and.returnValue(null); + + spyOn(contentApi, 'getFavorites').and.returnValue( + of({ + list: { + entries: [ + { + entry: { + target: { file: { id: 'file3', modifiedAt: new Date(3) } } + } + }, + { + entry: { + target: { file: { id: 'file1', modifiedAt: new Date(1) } } + } + }, + { + entry: { + target: { file: { id: 'file2', modifiedAt: new Date(2) } } + } + } + ] + } + }) + ); + + const ids = await component.getFileIds('favorites'); + expect(ids).toEqual(['file3', 'file2', 'file1']); + }); + + it('should fetch and sort file ids for shared files', async () => { + preferences.set('shared.sorting.key', 'name'); + preferences.set('shared.sorting.direction', 'asc'); + + spyOn(contentApi, 'findSharedLinks').and.returnValue( + of({ + list: { + entries: [ + { + entry: { + nodeId: 'node2', + name: 'node 2', + modifiedAt: new Date(2) + } + }, + { + entry: { + nodeId: 'node1', + name: 'node 1', + modifiedAt: new Date(1) + } + } + ] + } + }) + ); + + const ids = await component.getFileIds('shared'); + expect(ids).toEqual(['node1', 'node2']); + }); + + it('should sort file ids for favorites with [modifiedAt desc]', async () => { + spyOn(preferences, 'get').and.returnValue(null); + + spyOn(contentApi, 'findSharedLinks').and.returnValue( + of({ + list: { + entries: [ + { + entry: { + nodeId: 'node2', + name: 'node 2', + modifiedAt: new Date(2) + } + }, + { + entry: { + nodeId: 'node1', + name: 'node 1', + modifiedAt: new Date(1) + } + } + ] + } + }) + ); + + const ids = await component.getFileIds('shared'); + expect(ids).toEqual(['node2', 'node1']); + }); + + it('should fetch and sort ids for recent-files', async () => { + preferences.set('recent-files.sorting.key', 'name'); + preferences.set('recent-files.sorting.direction', 'asc'); + + spyOn(contentApi, 'getPerson').and.returnValue( + of({ + entry: { id: 'user' } + }) + ); + + spyOn(contentApi, 'search').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } }, + { entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } } + ] + } + }) + ); + + const ids = await component.getFileIds('recent-files'); + expect(ids).toEqual(['node1', 'node2']); + }); + + it('should sort file ids for favorites with [modifiedAt desc]', async () => { + spyOn(preferences, 'get').and.returnValue(null); + + spyOn(contentApi, 'getPerson').and.returnValue( + of({ + entry: { id: 'user' } + }) + ); + + spyOn(contentApi, 'search').and.returnValue( + of({ + list: { + entries: [ + { entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } }, + { entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } } + ] + } + }) + ); + + const ids = await component.getFileIds('recent-files'); + expect(ids).toEqual(['node2', 'node1']); + }); }); diff --git a/src/app/components/preview/preview.component.ts b/src/app/components/preview/preview.component.ts index 67e3d3176..dc872e35e 100644 --- a/src/app/components/preview/preview.component.ts +++ b/src/app/components/preview/preview.component.ts @@ -24,7 +24,14 @@ */ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { ActivatedRoute, Router, UrlTree, UrlSegmentGroup, UrlSegment, PRIMARY_OUTLET } from '@angular/router'; +import { + ActivatedRoute, + Router, + UrlTree, + UrlSegmentGroup, + UrlSegment, + PRIMARY_OUTLET +} from '@angular/router'; import { UserPreferencesService, ObjectUtils } from '@alfresco/adf-core'; import { Store } from '@ngrx/store'; import { AppStore } from '../../store/states/app.state'; @@ -37,324 +44,358 @@ import { ContentActionRef, ViewerExtensionRef } from '@alfresco/adf-extensions'; import { ViewUtilService } from './view-util.service'; @Component({ - selector: 'app-preview', - templateUrl: 'preview.component.html', - styleUrls: ['preview.component.scss'], - encapsulation: ViewEncapsulation.None, - host: { 'class': 'app-preview' } + selector: 'app-preview', + templateUrl: 'preview.component.html', + styleUrls: ['preview.component.scss'], + encapsulation: ViewEncapsulation.None, + host: { class: 'app-preview' } }) export class PreviewComponent extends PageComponent implements OnInit { + previewLocation: string = null; + routesSkipNavigation = ['shared', 'recent-files', 'favorites']; + navigateSource: string = null; + navigationSources = [ + 'favorites', + 'libraries', + 'personal-files', + 'recent-files', + 'shared' + ]; + folderId: string = null; + nodeId: string = null; + previousNodeId: string; + nextNodeId: string; + navigateMultiple = false; + openWith: Array = []; + contentExtensions: Array = []; - previewLocation: string = null; - routesSkipNavigation = [ 'shared', 'recent-files', 'favorites' ]; - navigateSource: string = null; - navigationSources = ['favorites', 'libraries', 'personal-files', 'recent-files', 'shared']; - folderId: string = null; - nodeId: string = null; - previousNodeId: string; - nextNodeId: string; - navigateMultiple = false; - openWith: Array = []; - contentExtensions: Array = []; + constructor( + private contentApi: ContentApiService, + private preferences: UserPreferencesService, + private route: ActivatedRoute, + private router: Router, + private viewUtils: ViewUtilService, + store: Store, + extensions: AppExtensionService, + content: ContentManagementService + ) { + super(store, extensions, content); + } - constructor( - private contentApi: ContentApiService, - private preferences: UserPreferencesService, - private route: ActivatedRoute, - private router: Router, - private viewUtils: ViewUtilService, - store: Store, - extensions: AppExtensionService, - content: ContentManagementService) { - super(store, extensions, content); + ngOnInit() { + super.ngOnInit(); + + this.previewLocation = this.router.url + .substr(0, this.router.url.indexOf('/', 1)) + .replace(/\//g, ''); + + const routeData = this.route.snapshot.data; + + if (routeData.navigateMultiple) { + this.navigateMultiple = true; } - ngOnInit() { - super.ngOnInit(); - - this.previewLocation = this.router.url - .substr(0, this.router.url.indexOf('/', 1)) - .replace(/\//g, ''); - - const routeData = this.route.snapshot.data; - - if (routeData.navigateMultiple) { - this.navigateMultiple = true; - } - - if (routeData.navigateSource) { - const source = routeData.navigateSource.toLowerCase(); - if (this.navigationSources.includes(source)) { - this.navigateSource = routeData.navigateSource; - } - } - - this.route.params.subscribe(params => { - this.folderId = params.folderId; - const id = params.nodeId; - if (id) { - this.displayNode(id); - } - }); - - this.openWith = this.extensions.openWithActions; - this.contentExtensions = this.extensions.viewerContentExtensions; + if (routeData.navigateSource) { + const source = routeData.navigateSource.toLowerCase(); + if (this.navigationSources.includes(source)) { + this.navigateSource = routeData.navigateSource; + } } - /** - * Loads the particular node into the Viewer - * @param id Unique identifier for the Node to display - */ - async displayNode(id: string) { - if (id) { - try { - this.node = await this.contentApi.getNodeInfo(id).toPromise(); - this.store.dispatch(new SetSelectedNodesAction([{ entry: this.node }])); + this.route.params.subscribe(params => { + this.folderId = params.folderId; + const id = params.nodeId; + if (id) { + this.displayNode(id); + } + }); - if (this.node && this.node.isFile) { - const nearest = await this.getNearestNodes(this.node.id, this.node.parentId); + this.openWith = this.extensions.openWithActions; + this.contentExtensions = this.extensions.viewerContentExtensions; + } - this.previousNodeId = nearest.left; - this.nextNodeId = nearest.right; - this.nodeId = this.node.id; - return; - } - this.router.navigate([this.previewLocation, id]); - } catch (err) { - if (!err || err.status !== 401) { - this.router.navigate([this.previewLocation, id]); - } - } + /** + * Loads the particular node into the Viewer + * @param id Unique identifier for the Node to display + */ + async displayNode(id: string) { + if (id) { + try { + this.node = await this.contentApi.getNodeInfo(id).toPromise(); + this.store.dispatch(new SetSelectedNodesAction([{ entry: this.node }])); + + if (this.node && this.node.isFile) { + const nearest = await this.getNearestNodes( + this.node.id, + this.node.parentId + ); + + this.previousNodeId = nearest.left; + this.nextNodeId = nearest.right; + this.nodeId = this.node.id; + return; } + this.router.navigate([this.previewLocation, id]); + } catch (err) { + if (!err || err.status !== 401) { + this.router.navigate([this.previewLocation, id]); + } + } + } + } + + /** + * Handles the visibility change of the Viewer component. + * @param isVisible Indicator whether Viewer is visible or hidden. + */ + onVisibilityChanged(isVisible: boolean): void { + const shouldSkipNavigation = this.routesSkipNavigation.includes( + this.previewLocation + ); + + if (!isVisible) { + const route = this.getNavigationCommands(this.previewLocation); + + if (!shouldSkipNavigation && this.folderId) { + route.push(this.folderId); + } + + this.router.navigate(route); + } + } + + /** Handles navigation to a previous document */ + onNavigateBefore(): void { + if (this.previousNodeId) { + this.router.navigate( + this.getPreviewPath(this.folderId, this.previousNodeId) + ); + } + } + + /** Handles navigation to a next document */ + onNavigateNext(): void { + if (this.nextNodeId) { + this.router.navigate(this.getPreviewPath(this.folderId, this.nextNodeId)); + } + } + + /** + * Generates a node preview route based on folder and node IDs. + * @param folderId Folder ID + * @param nodeId Node ID + */ + getPreviewPath(folderId: string, nodeId: string): any[] { + const route = [this.previewLocation]; + + if (folderId) { + route.push(folderId); } - /** - * Handles the visibility change of the Viewer component. - * @param isVisible Indicator whether Viewer is visible or hidden. - */ - onVisibilityChanged(isVisible: boolean): void { - const shouldSkipNavigation = this.routesSkipNavigation.includes(this.previewLocation); - - if (!isVisible) { - const route = this.getNavigationCommands(this.previewLocation); - - if ( !shouldSkipNavigation && this.folderId ) { - route.push(this.folderId); - } - - this.router.navigate(route); - } + if (nodeId) { + route.push('preview', nodeId); } - /** Handles navigation to a previous document */ - onNavigateBefore(): void { - if (this.previousNodeId) { - this.router.navigate( - this.getPreviewPath(this.folderId, this.previousNodeId) - ); - } - } + return route; + } - /** Handles navigation to a next document */ - onNavigateNext(): void { - if (this.nextNodeId) { - this.router.navigate( - this.getPreviewPath(this.folderId, this.nextNodeId) - ); - } - } + /** + * Retrieves nearest node information for the given node and folder. + * @param nodeId Unique identifier of the document node + * @param folderId Unique identifier of the containing folder node. + */ + async getNearestNodes( + nodeId: string, + folderId: string + ): Promise<{ left: string; right: string }> { + const empty = { + left: null, + right: null + }; - /** - * Generates a node preview route based on folder and node IDs. - * @param folderId Folder ID - * @param nodeId Node ID - */ - getPreviewPath(folderId: string, nodeId: string): any[] { - const route = [this.previewLocation]; + if (nodeId && folderId) { + try { + const ids = await this.getFileIds(this.navigateSource, folderId); + const idx = ids.indexOf(nodeId); - if (folderId) { - route.push(folderId); - } - - if (nodeId) { - route.push('preview', nodeId); - } - - return route; - } - - - /** - * Retrieves nearest node information for the given node and folder. - * @param nodeId Unique identifier of the document node - * @param folderId Unique identifier of the containing folder node. - */ - async getNearestNodes(nodeId: string, folderId: string): Promise<{ left: string, right: string }> { - const empty = { - left: null, - right: null - }; - - if (nodeId && folderId) { - try { - const ids = await this.getFileIds(this.navigateSource, folderId); - const idx = ids.indexOf(nodeId); - - if (idx >= 0) { - return { - left: ids[idx - 1] || null, - right: ids[idx + 1] || null - }; - } else { - return empty; - } - } catch { - return empty; - } + if (idx >= 0) { + return { + left: ids[idx - 1] || null, + right: ids[idx + 1] || null + }; } else { - return empty; + return empty; } + } catch { + return empty; + } + } else { + return empty; + } + } + + /** + * Retrieves a list of node identifiers for the folder and data source. + * @param source Data source name. Allowed values are: personal-files, libraries, favorites, shared, recent-files. + * @param folderId Containing folder node identifier for 'personal-files' and 'libraries' sources. + */ + async getFileIds(source: string, folderId?: string): Promise { + if ((source === 'personal-files' || source === 'libraries') && folderId) { + const sortKey = + this.preferences.get('personal-files.sorting.key') || 'modifiedAt'; + const sortDirection = + this.preferences.get('personal-files.sorting.direction') || 'desc'; + const nodes = await this.contentApi + .getNodeChildren(folderId, { + // orderBy: `${sortKey} ${sortDirection}`, + fields: ['id', this.getRootField(sortKey)], + where: '(isFile=true)' + }) + .toPromise(); + + const entries = nodes.list.entries.map(obj => obj.entry); + this.sort(entries, sortKey, sortDirection); + + return entries.map(obj => obj.id); } - /** - * Retrieves a list of node identifiers for the folder and data source. - * @param source Data source name. Allowed values are: personal-files, libraries, favorites, shared, recent-files. - * @param folderId Containing folder node identifier for 'personal-files' and 'libraries' sources. - */ - async getFileIds(source: string, folderId?: string): Promise { - if ((source === 'personal-files' || source === 'libraries') && folderId) { - const sortKey = this.preferences.get('personal-files.sorting.key') || 'modifiedAt'; - const sortDirection = this.preferences.get('personal-files.sorting.direction') || 'desc'; - const nodes = await this.contentApi.getNodeChildren(folderId, { - // orderBy: `${sortKey} ${sortDirection}`, - fields: ['id', this.getRootField(sortKey)], - where: '(isFile=true)' - }).toPromise(); + if (source === 'favorites') { + const nodes = await this.contentApi + .getFavorites('-me-', { + where: '(EXISTS(target/file))', + fields: ['target'] + }) + .toPromise(); - const entries = nodes.list.entries.map(obj => obj.entry); - this.sort(entries, sortKey, sortDirection); + const sortKey = + this.preferences.get('favorites.sorting.key') || 'modifiedAt'; + const sortDirection = + this.preferences.get('favorites.sorting.direction') || 'desc'; + const files = nodes.list.entries.map(obj => obj.entry.target.file); + this.sort(files, sortKey, sortDirection); - return entries.map(obj => obj.id); - } - - if (source === 'favorites') { - const nodes = await this.contentApi.getFavorites('-me-', { - where: '(EXISTS(target/file))', - fields: ['target'] - }).toPromise(); - - const sortKey = this.preferences.get('favorites.sorting.key') || 'modifiedAt'; - const sortDirection = this.preferences.get('favorites.sorting.direction') || 'desc'; - const files = nodes.list.entries.map(obj => obj.entry.target.file); - this.sort(files, sortKey, sortDirection); - - return files.map(f => f.id); - } - - if (source === 'shared') { - const sortingKey = this.preferences.get('shared.sorting.key') || 'modifiedAt'; - const sortingDirection = this.preferences.get('shared.sorting.direction') || 'desc'; - - const nodes = await this.contentApi.findSharedLinks({ - fields: ['nodeId', this.getRootField(sortingKey)] - }).toPromise(); - - const entries = nodes.list.entries.map(obj => obj.entry); - this.sort(entries, sortingKey, sortingDirection); - - return entries.map(obj => obj.nodeId); - } - - if (source === 'recent-files') { - const person = await this.contentApi.getPerson('-me-').toPromise(); - const username = person.entry.id; - const sortingKey = this.preferences.get('recent-files.sorting.key') || 'modifiedAt'; - const sortingDirection = this.preferences.get('recent-files.sorting.direction') || 'desc'; - - const nodes = await this.contentApi.search({ - query: { - query: '*', - language: 'afts' - }, - filterQueries: [ - { query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` }, - { query: `cm:modifier:${username} OR cm:creator:${username}` }, - { query: `TYPE:"content" AND -TYPE:"app:filelink" AND -TYPE:"fm:post"` } - ], - fields: ['id', this.getRootField(sortingKey)], - sort: [{ - type: 'FIELD', - field: 'cm:modified', - ascending: false - }] - }).toPromise(); - - const entries = nodes.list.entries.map(obj => obj.entry); - this.sort(entries, sortingKey, sortingDirection); - - return entries.map(obj => obj.id); - } - - return []; + return files.map(f => f.id); } - private sort(items: any[], key: string, direction: string) { - const options: Intl.CollatorOptions = {}; + if (source === 'shared') { + const sortingKey = + this.preferences.get('shared.sorting.key') || 'modifiedAt'; + const sortingDirection = + this.preferences.get('shared.sorting.direction') || 'desc'; - if (key.includes('sizeInBytes') || key === 'name') { - options.numeric = true; - } + const nodes = await this.contentApi + .findSharedLinks({ + fields: ['nodeId', this.getRootField(sortingKey)] + }) + .toPromise(); - items.sort((a: any, b: any) => { - let left = ObjectUtils.getValue(a, key); - if (left) { - left = (left instanceof Date) ? left.valueOf().toString() : left.toString(); - } else { - left = ''; + const entries = nodes.list.entries.map(obj => obj.entry); + this.sort(entries, sortingKey, sortingDirection); + + return entries.map(obj => obj.nodeId); + } + + if (source === 'recent-files') { + const person = await this.contentApi.getPerson('-me-').toPromise(); + const username = person.entry.id; + const sortingKey = + this.preferences.get('recent-files.sorting.key') || 'modifiedAt'; + const sortingDirection = + this.preferences.get('recent-files.sorting.direction') || 'desc'; + + const nodes = await this.contentApi + .search({ + query: { + query: '*', + language: 'afts' + }, + filterQueries: [ + { query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` }, + { query: `cm:modifier:${username} OR cm:creator:${username}` }, + { + query: `TYPE:"content" AND -TYPE:"app:filelink" AND -TYPE:"fm:post"` } - - let right = ObjectUtils.getValue(b, key); - if (right) { - right = (right instanceof Date) ? right.valueOf().toString() : right.toString(); - } else { - right = ''; + ], + fields: ['id', this.getRootField(sortingKey)], + sort: [ + { + type: 'FIELD', + field: 'cm:modified', + ascending: false } + ] + }) + .toPromise(); - return direction === 'asc' - ? left.localeCompare(right, undefined, options) - : right.localeCompare(left, undefined, options); - }); + const entries = nodes.list.entries.map(obj => obj.entry); + this.sort(entries, sortingKey, sortingDirection); + + return entries.map(obj => obj.id); } - /** - * Get the root field name from the property path. - * Example: 'property1.some.child.property' => 'property1' - * @param path Property path - */ - getRootField(path: string) { - if (path) { - return path.split('.')[0]; - } - return path; + return []; + } + + private sort(items: any[], key: string, direction: string) { + const options: Intl.CollatorOptions = {}; + + if (key.includes('sizeInBytes') || key === 'name') { + options.numeric = true; } - printFile() { - this.viewUtils.printFileGeneric(this.nodeId, this.node.content.mimeType); + items.sort((a: any, b: any) => { + let left = ObjectUtils.getValue(a, key); + if (left) { + left = + left instanceof Date ? left.valueOf().toString() : left.toString(); + } else { + left = ''; + } + + let right = ObjectUtils.getValue(b, key); + if (right) { + right = + right instanceof Date ? right.valueOf().toString() : right.toString(); + } else { + right = ''; + } + + return direction === 'asc' + ? left.localeCompare(right, undefined, options) + : right.localeCompare(left, undefined, options); + }); + } + + /** + * Get the root field name from the property path. + * Example: 'property1.some.child.property' => 'property1' + * @param path Property path + */ + getRootField(path: string) { + if (path) { + return path.split('.')[0]; + } + return path; + } + + printFile() { + this.viewUtils.printFileGeneric(this.nodeId, this.node.content.mimeType); + } + + private getNavigationCommands(url: string): any[] { + const urlTree: UrlTree = this.router.parseUrl(url); + const urlSegmentGroup: UrlSegmentGroup = + urlTree.root.children[PRIMARY_OUTLET]; + + if (!urlSegmentGroup) { + return [url]; } - private getNavigationCommands(url: string): any[] { - const urlTree: UrlTree = this.router.parseUrl(url); - const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET]; + const urlSegments: UrlSegment[] = urlSegmentGroup.segments; - if (!urlSegmentGroup) { - return [url]; - } - - const urlSegments: UrlSegment[] = urlSegmentGroup.segments; - - return urlSegments.reduce(function(acc, item) { - acc.push(item.path, item.parameters); - return acc; - }, []); - } + return urlSegments.reduce(function(acc, item) { + acc.push(item.path, item.parameters); + return acc; + }, []); + } } diff --git a/src/app/components/preview/preview.module.ts b/src/app/components/preview/preview.module.ts index 9e3adcf2c..59dc05ab1 100644 --- a/src/app/components/preview/preview.module.ts +++ b/src/app/components/preview/preview.module.ts @@ -37,32 +37,25 @@ import { PreviewExtensionComponent } from './preview-extension.component'; import { AppToolbarModule } from '../toolbar/toolbar.module'; const routes: Routes = [ - { - path: '', - component: PreviewComponent - } + { + path: '', + component: PreviewComponent + } ]; @NgModule({ - imports: [ - CommonModule, - RouterModule.forChild(routes), - CoreModule.forChild(), - ContentDirectiveModule, - DirectivesModule, - AppInfoDrawerModule, - CoreExtensionsModule.forChild(), - AppToolbarModule - ], - declarations: [ - PreviewComponent, - PreviewExtensionComponent - ], - providers: [ - ViewUtilService - ], - exports: [ - PreviewComponent - ] + imports: [ + CommonModule, + RouterModule.forChild(routes), + CoreModule.forChild(), + ContentDirectiveModule, + DirectivesModule, + AppInfoDrawerModule, + CoreExtensionsModule.forChild(), + AppToolbarModule + ], + declarations: [PreviewComponent, PreviewExtensionComponent], + providers: [ViewUtilService], + exports: [PreviewComponent] }) export class PreviewModule {} diff --git a/src/app/components/preview/view-util.service.ts b/src/app/components/preview/view-util.service.ts index 36ba6760e..c9320b63f 100644 --- a/src/app/components/preview/view-util.service.ts +++ b/src/app/components/preview/view-util.service.ts @@ -1,183 +1,228 @@ -import {Injectable} from '@angular/core'; -import {AlfrescoApiService, LogService} from '@alfresco/adf-core'; -import {RenditionEntry} from 'alfresco-js-api'; -import {ContentApiService} from './../../services/content-api.service'; +import { Injectable } from '@angular/core'; +import { AlfrescoApiService, LogService } from '@alfresco/adf-core'; +import { RenditionEntry } from 'alfresco-js-api'; +import { ContentApiService } from './../../services/content-api.service'; @Injectable() export class ViewUtilService { - static TARGET = '_new'; + static TARGET = '_new'; - /** - * Content groups based on categorization of files that can be viewed in the web browser. This - * implementation or grouping is tied to the definition the ng component: ViewerComponent - */ - public static ContentGroup = { - IMAGE: 'image', - MEDIA: 'media', - PDF: 'pdf', - TEXT: 'text' - }; + /** + * Content groups based on categorization of files that can be viewed in the web browser. This + * implementation or grouping is tied to the definition the ng component: ViewerComponent + */ + public static ContentGroup = { + IMAGE: 'image', + MEDIA: 'media', + PDF: 'pdf', + TEXT: 'text' + }; - /** - * Based on ViewerComponent Implementation, this value is used to determine how many times we try - * to get the rendition of a file for preview, or printing. - * @type {number} - */ - maxRetries = 5; + /** + * Based on ViewerComponent Implementation, this value is used to determine how many times we try + * to get the rendition of a file for preview, or printing. + * @type {number} + */ + maxRetries = 5; - /** - * Mime-type grouping based on the ViewerComponent. - */ - private mimeTypes = { - text: ['text/plain', 'text/csv', 'text/xml', 'text/html', 'application/x-javascript'], - pdf: ['application/pdf'], - image: ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/svg+xml'], - media: ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/ogg', 'audio/wav'] - }; + /** + * Mime-type grouping based on the ViewerComponent. + */ + private mimeTypes = { + text: [ + 'text/plain', + 'text/csv', + 'text/xml', + 'text/html', + 'application/x-javascript' + ], + pdf: ['application/pdf'], + image: [ + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/bmp', + 'image/svg+xml' + ], + media: [ + 'video/mp4', + 'video/webm', + 'video/ogg', + 'audio/mpeg', + 'audio/ogg', + 'audio/wav' + ] + }; - constructor(private apiService: AlfrescoApiService, - private contentApi: ContentApiService, - private logService: LogService) { + constructor( + private apiService: AlfrescoApiService, + private contentApi: ContentApiService, + private logService: LogService + ) {} + + /** + * This method takes a url to trigger the print dialog against, and the type of artifact that it + * is. + * This URL should be one that can be rendered in the browser, for example PDF, Image, or Text + * @param {string} url + * @param {string} type + */ + public printFile(url: string, type: string) { + const pwa = window.open(url, ViewUtilService.TARGET); + // Because of the way chrome focus and close image window vs. pdf preview window + if (type === ViewUtilService.ContentGroup.IMAGE) { + pwa.onfocus = () => { + setTimeout(() => { + pwa.close(); + }, 500); + }; + pwa.onload = () => { + pwa.print(); + }; + } else { + pwa.onload = () => { + pwa.print(); + pwa.onfocus = () => { + setTimeout(() => { + pwa.close(); + }, 10); + }; + }; } + } - /** - * This method takes a url to trigger the print dialog against, and the type of artifact that it - * is. - * This URL should be one that can be rendered in the browser, for example PDF, Image, or Text - * @param {string} url - * @param {string} type - */ - public printFile(url: string, type: string) { - const pwa = window.open(url, ViewUtilService.TARGET); - // Because of the way chrome focus and close image window vs. pdf preview window - if (type === ViewUtilService.ContentGroup.IMAGE) { - pwa.onfocus = () => { - setTimeout( () => { - pwa.close(); - }, 500); - }; - pwa.onload = () => { - pwa.print(); - }; - } else { - pwa.onload = () => { - pwa.print(); - pwa.onfocus = () => { - setTimeout( () => { - pwa.close(); - }, 10); - }; - }; + /** + * Launch the File Print dialog from anywhere other than the preview service, which resolves the + * rendition of the object that can be printed from a web browser. + * These are: images, PDF files, or PDF rendition of files. + * We also force PDF rendition for TEXT type objects, otherwise the default URL is to download. + * TODO there are different TEXT type objects, (HTML, plaintext, xml, etc. we should determine how these are handled) + * @param {string} objectId + * @param {string} objectType + */ + public printFileGeneric(objectId: string, mimeType: string) { + const nodeId = objectId; + const type: string = this.getViewerTypeByMimeType(mimeType); + + this.getRendition(nodeId, ViewUtilService.ContentGroup.PDF) + .then(value => { + const url: string = this.getRenditionUrl( + nodeId, + type, + value ? true : false + ); + const printType = + type === ViewUtilService.ContentGroup.PDF || + type === ViewUtilService.ContentGroup.TEXT + ? ViewUtilService.ContentGroup.PDF + : type; + this.printFile(url, printType); + }) + .catch(err => { + this.logService.error('Error with Printing'); + this.logService.error(err); + }); + } + + public getRenditionUrl( + nodeId: string, + type: string, + renditionExists: boolean + ): string { + return renditionExists && type !== ViewUtilService.ContentGroup.IMAGE + ? this.apiService.contentApi.getRenditionUrl( + nodeId, + ViewUtilService.ContentGroup.PDF + ) + : this.contentApi.getContentUrl(nodeId, false); + } + + /** + * From ViewerComponent + * @param {string} nodeId + * @param {string} renditionId + * @param {number} retries + * @returns {Promise} + */ + private async waitRendition( + nodeId: string, + renditionId: string, + retries: number + ): Promise { + const rendition = await this.apiService.renditionsApi.getRendition( + nodeId, + renditionId + ); + + if (this.maxRetries < retries) { + const status = rendition.entry.status.toString(); + + if (status === 'CREATED') { + return rendition; + } else { + retries += 1; + await this.wait(1000); + return await this.waitRendition(nodeId, renditionId, retries); + } + } + } + + /** + * From ViewerComponent + * @param {string} mimeType + * @returns {string} + */ + getViewerTypeByMimeType(mimeType: string) { + if (mimeType) { + mimeType = mimeType.toLowerCase(); + + const editorTypes = Object.keys(this.mimeTypes); + for (const type of editorTypes) { + if (this.mimeTypes[type].indexOf(mimeType) >= 0) { + return type; } + } } + return 'unknown'; + } - /** - * Launch the File Print dialog from anywhere other than the preview service, which resolves the - * rendition of the object that can be printed from a web browser. - * These are: images, PDF files, or PDF rendition of files. - * We also force PDF rendition for TEXT type objects, otherwise the default URL is to download. - * TODO there are different TEXT type objects, (HTML, plaintext, xml, etc. we should determine how these are handled) - * @param {string} objectId - * @param {string} objectType - */ - public printFileGeneric(objectId: string, mimeType: string) { - const nodeId = objectId; - const type: string = this.getViewerTypeByMimeType(mimeType); + /** + * From ViewerComponent + * @param {number} ms + * @returns {Promise} + */ + public wait(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } - this.getRendition(nodeId, ViewUtilService.ContentGroup.PDF) - .then(value => { - const url: string = this.getRenditionUrl(nodeId, type, (value ? true : false)); - const printType = (type === ViewUtilService.ContentGroup.PDF - || type === ViewUtilService.ContentGroup.TEXT) - ? ViewUtilService.ContentGroup.PDF : type; - this.printFile(url, printType); - }) - .catch(err => { - this.logService.error('Error with Printing'); - this.logService.error(err); - }); - } + /** + * From ViewerComponent + * @param {string} nodeId + * @returns {string} + */ + public async getRendition( + nodeId: string, + renditionId: string + ): Promise { + const supported = await this.apiService.renditionsApi.getRenditions(nodeId); + let rendition = supported.list.entries.find( + obj => obj.entry.id.toLowerCase() === renditionId + ); - public getRenditionUrl(nodeId: string, type: string, renditionExists: boolean): string { - return (renditionExists && type !== ViewUtilService.ContentGroup.IMAGE) ? - this.apiService.contentApi.getRenditionUrl(nodeId, ViewUtilService.ContentGroup.PDF) : - this.contentApi.getContentUrl(nodeId, false); - } + if (rendition) { + const status = rendition.entry.status.toString(); - /** - * From ViewerComponent - * @param {string} nodeId - * @param {string} renditionId - * @param {number} retries - * @returns {Promise} - */ - private async waitRendition(nodeId: string, renditionId: string, retries: number): Promise { - const rendition = await this.apiService.renditionsApi.getRendition(nodeId, renditionId); - - if (this.maxRetries < retries) { - const status = rendition.entry.status.toString(); - - if (status === 'CREATED') { - return rendition; - } else { - retries += 1; - await this.wait(1000); - return await this.waitRendition(nodeId, renditionId, retries); - } + if (status === 'NOT_CREATED') { + try { + await this.apiService.renditionsApi.createRendition(nodeId, { + id: renditionId + }); + rendition = await this.waitRendition(nodeId, renditionId, 0); + } catch (err) { + this.logService.error(err); } + } } - - - /** - * From ViewerComponent - * @param {string} mimeType - * @returns {string} - */ - getViewerTypeByMimeType(mimeType: string) { - if (mimeType) { - mimeType = mimeType.toLowerCase(); - - const editorTypes = Object.keys(this.mimeTypes); - for (const type of editorTypes) { - if (this.mimeTypes[type].indexOf(mimeType) >= 0) { - return type; - } - } - } - return 'unknown'; - } - - /** - * From ViewerComponent - * @param {number} ms - * @returns {Promise} - */ - public wait(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - /** - * From ViewerComponent - * @param {string} nodeId - * @returns {string} - */ - public async getRendition(nodeId: string, renditionId: string): Promise { - const supported = await this.apiService.renditionsApi.getRenditions(nodeId); - let rendition = supported.list.entries.find(obj => obj.entry.id.toLowerCase() === renditionId); - - if (rendition) { - const status = rendition.entry.status.toString(); - - if (status === 'NOT_CREATED') { - try { - await this.apiService.renditionsApi.createRendition(nodeId, {id: renditionId}); - rendition = await this.waitRendition(nodeId, renditionId, 0); - } catch (err) { - this.logService.error(err); - } - } - } - return new Promise(resolve => resolve(rendition)); - } - + return new Promise(resolve => resolve(rendition)); + } } diff --git a/src/app/components/recent-files/recent-files.component.spec.ts b/src/app/components/recent-files/recent-files.component.spec.ts index b359aea59..525081ae1 100644 --- a/src/app/components/recent-files/recent-files.component.spec.ts +++ b/src/app/components/recent-files/recent-files.component.spec.ts @@ -26,8 +26,12 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { - AlfrescoApiService, - TimeAgoPipe, NodeNameTooltipPipe, NodeFavoriteDirective, DataTableComponent, AppConfigPipe + AlfrescoApiService, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DataTableComponent, + AppConfigPipe } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../services/content-management.service'; @@ -37,91 +41,93 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { ExperimentalDirective } from '../../directives/experimental.directive'; describe('RecentFilesComponent', () => { - let fixture: ComponentFixture; - let component: RecentFilesComponent; - let alfrescoApi: AlfrescoApiService; - let contentService: ContentManagementService; - let page; + let fixture: ComponentFixture; + let component: RecentFilesComponent; + let alfrescoApi: AlfrescoApiService; + let contentService: ContentManagementService; + let page; + beforeEach(() => { + page = { + list: { + entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }], + pagination: { data: 'data' } + } + }; + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + DataTableComponent, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DocumentListComponent, + RecentFilesComponent, + AppConfigPipe, + ExperimentalDirective + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(RecentFilesComponent); + component = fixture.componentInstance; + + contentService = TestBed.get(ContentManagementService); + alfrescoApi = TestBed.get(AlfrescoApiService); + alfrescoApi.reset(); + + spyOn(alfrescoApi.peopleApi, 'getPerson').and.returnValue( + Promise.resolve({ + entry: { id: 'personId' } + }) + ); + + spyOn(alfrescoApi.searchApi, 'search').and.returnValue( + Promise.resolve(page) + ); + }); + + describe('OnInit()', () => { beforeEach(() => { - page = { - list: { - entries: [ { entry: { id: 1 } }, { entry: { id: 2 } } ], - pagination: { data: 'data'} - } - }; + spyOn(component, 'reload').and.stub(); }); - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - AppTestingModule - ], - declarations: [ - DataTableComponent, - TimeAgoPipe, - NodeNameTooltipPipe, - NodeFavoriteDirective, - DocumentListComponent, - RecentFilesComponent, - AppConfigPipe, - ExperimentalDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); + it('should reload nodes on onDeleteNode event', () => { + fixture.detectChanges(); - fixture = TestBed.createComponent(RecentFilesComponent); - component = fixture.componentInstance; + contentService.nodesDeleted.next(); - contentService = TestBed.get(ContentManagementService); - alfrescoApi = TestBed.get(AlfrescoApiService); - alfrescoApi.reset(); - - spyOn(alfrescoApi.peopleApi, 'getPerson').and.returnValue(Promise.resolve({ - entry: { id: 'personId' } - })); - - spyOn(alfrescoApi.searchApi, 'search').and.returnValue(Promise.resolve(page)); + expect(component.reload).toHaveBeenCalled(); }); - describe('OnInit()', () => { - beforeEach(() => { - spyOn(component, 'reload').and.stub(); - }); + it('should reload on onRestoreNode event', () => { + fixture.detectChanges(); - it('should reload nodes on onDeleteNode event', () => { - fixture.detectChanges(); + contentService.nodesRestored.next(); - contentService.nodesDeleted.next(); - - expect(component.reload).toHaveBeenCalled(); - }); - - it('should reload on onRestoreNode event', () => { - fixture.detectChanges(); - - contentService.nodesRestored.next(); - - expect(component.reload).toHaveBeenCalled(); - }); - - it('should reload on move node event', () => { - fixture.detectChanges(); - - contentService.nodesMoved.next(); - - expect(component.reload).toHaveBeenCalled(); - }); + expect(component.reload).toHaveBeenCalled(); }); - describe('refresh', () => { - it('should call document list reload', () => { - spyOn(component.documentList, 'reload'); - fixture.detectChanges(); + it('should reload on move node event', () => { + fixture.detectChanges(); - component.reload(); + contentService.nodesMoved.next(); - expect(component.documentList.reload).toHaveBeenCalled(); - }); + expect(component.reload).toHaveBeenCalled(); }); + }); + + describe('refresh', () => { + it('should call document list reload', () => { + spyOn(component.documentList, 'reload'); + fixture.detectChanges(); + + component.reload(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/components/recent-files/recent-files.component.ts b/src/app/components/recent-files/recent-files.component.ts index faaef0b87..283483f93 100644 --- a/src/app/components/recent-files/recent-files.component.ts +++ b/src/app/components/recent-files/recent-files.component.ts @@ -33,47 +33,44 @@ import { AppStore } from '../../store/states/app.state'; import { AppExtensionService } from '../../extensions/extension.service'; @Component({ - templateUrl: './recent-files.component.html' + templateUrl: './recent-files.component.html' }) export class RecentFilesComponent extends PageComponent implements OnInit { - isSmallScreen = false; + isSmallScreen = false; - constructor( - store: Store, - extensions: AppExtensionService, - content: ContentManagementService, - private breakpointObserver: BreakpointObserver - ) { - super(store, extensions, content); - } + constructor( + store: Store, + extensions: AppExtensionService, + content: ContentManagementService, + private breakpointObserver: BreakpointObserver + ) { + super(store, extensions, content); + } - ngOnInit() { - super.ngOnInit(); + ngOnInit() { + super.ngOnInit(); - this.subscriptions = this.subscriptions.concat([ - this.content.nodesDeleted.subscribe(() => this.reload()), - this.content.nodesMoved.subscribe(() => this.reload()), - this.content.nodesRestored.subscribe(() => this.reload()), + this.subscriptions = this.subscriptions.concat([ + this.content.nodesDeleted.subscribe(() => this.reload()), + this.content.nodesMoved.subscribe(() => this.reload()), + this.content.nodesRestored.subscribe(() => this.reload()), - this.breakpointObserver - .observe([ - Breakpoints.HandsetPortrait, - Breakpoints.HandsetLandscape - ]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }) - ]); - } + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ]); + } - onNodeDoubleClick(node: MinimalNodeEntity) { - if (node && node.entry) { - if (PageComponent.isLockedNode(node.entry)) { - event.preventDefault(); - return; - } + onNodeDoubleClick(node: MinimalNodeEntity) { + if (node && node.entry) { + if (PageComponent.isLockedNode(node.entry)) { + event.preventDefault(); + return; + } - this.showPreview(node); - } + this.showPreview(node); } + } } diff --git a/src/app/components/search/search-input-control/search-input-control.component.scss b/src/app/components/search/search-input-control/search-input-control.component.scss index 3dc6f9feb..a88d9d0db 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.scss +++ b/src/app/components/search/search-input-control/search-input-control.component.scss @@ -1,8 +1,8 @@ .adf-clear-search-icon-wrapper { - width: 1em; + width: 1em; - .mat-icon { - font-size: 100%; - cursor: pointer; - } + .mat-icon { + font-size: 100%; + cursor: pointer; + } } diff --git a/src/app/components/search/search-input-control/search-input-control.component.ts b/src/app/components/search/search-input-control/search-input-control.component.ts index c42de3bca..145a74ec6 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.ts +++ b/src/app/components/search/search-input-control/search-input-control.component.ts @@ -24,253 +24,299 @@ */ import { ThumbnailService } from '@alfresco/adf-core'; -import { animate, state, style, transition, trigger } from '@angular/animations'; -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, - QueryList, ViewEncapsulation, ViewChild, ViewChildren, ElementRef, TemplateRef, ContentChild } from '@angular/core'; +import { + animate, + state, + style, + transition, + trigger +} from '@angular/animations'; +import { + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + QueryList, + ViewEncapsulation, + ViewChild, + ViewChildren, + ElementRef, + TemplateRef, + ContentChild +} from '@angular/core'; import { MinimalNodeEntity, QueryBody } from 'alfresco-js-api'; import { Subject } from 'rxjs'; import { MatListItem } from '@angular/material'; import { debounceTime, filter } from 'rxjs/operators'; -import { EmptySearchResultComponent, SearchComponent } from '@alfresco/adf-content-services'; +import { + EmptySearchResultComponent, + SearchComponent +} from '@alfresco/adf-content-services'; @Component({ - selector: 'app-search-input-control', - templateUrl: './search-input-control.component.html', - styleUrls: ['./search-input-control.component.scss'], - animations: [ - trigger('transitionMessages', [ - state('active', style({ transform: 'translateX(0%)', 'margin-left': '13px' })), - state('inactive', style({ transform: 'translateX(81%)'})), - state('no-animation', style({ transform: 'translateX(0%)', width: '100%' })), - transition('inactive => active', - animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')), - transition('active => inactive', - animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')) - ]) - ], - encapsulation: ViewEncapsulation.None, - host: { class: 'adf-search-control' } + selector: 'app-search-input-control', + templateUrl: './search-input-control.component.html', + styleUrls: ['./search-input-control.component.scss'], + animations: [ + trigger('transitionMessages', [ + state( + 'active', + style({ transform: 'translateX(0%)', 'margin-left': '13px' }) + ), + state('inactive', style({ transform: 'translateX(81%)' })), + state( + 'no-animation', + style({ transform: 'translateX(0%)', width: '100%' }) + ), + transition( + 'inactive => active', + animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)') + ), + transition( + 'active => inactive', + animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)') + ) + ]) + ], + encapsulation: ViewEncapsulation.None, + host: { class: 'adf-search-control' } }) export class SearchInputControlComponent implements OnInit, OnDestroy { + /** Toggles whether to use an expanding search control. If false + * then a regular input is used. + */ + @Input() + expandable = true; - /** Toggles whether to use an expanding search control. If false - * then a regular input is used. - */ - @Input() - expandable = true; + /** Toggles highlighting of the search term in the results. */ + @Input() + highlight = false; - /** Toggles highlighting of the search term in the results. */ - @Input() - highlight = false; + /** Type of the input field to render, e.g. "search" or "text" (default). */ + @Input() + inputType = 'text'; - /** Type of the input field to render, e.g. "search" or "text" (default). */ - @Input() - inputType = 'text'; + /** Toggles auto-completion of the search input field. */ + @Input() + autocomplete = false; - /** Toggles auto-completion of the search input field. */ - @Input() - autocomplete = false; + /** Toggles "find-as-you-type" suggestions for possible matches. */ + @Input() + liveSearchEnabled = true; - /** Toggles "find-as-you-type" suggestions for possible matches. */ - @Input() - liveSearchEnabled = true; + /** Maximum number of results to show in the live search. */ + @Input() + liveSearchMaxResults = 5; - /** Maximum number of results to show in the live search. */ - @Input() - liveSearchMaxResults = 5; + /** @deprecated in 2.1.0 */ + @Input() + customQueryBody: QueryBody; - /** @deprecated in 2.1.0 */ - @Input() - customQueryBody: QueryBody; + /** Emitted when the search is submitted pressing ENTER button. + * The search term is provided as value of the event. + */ + @Output() + submit: EventEmitter = new EventEmitter(); - /** Emitted when the search is submitted pressing ENTER button. - * The search term is provided as value of the event. - */ - @Output() - submit: EventEmitter = new EventEmitter(); + /** Emitted when the search term is changed. The search term is provided + * in the 'value' property of the returned object. If the term is less + * than three characters in length then the term is truncated to an empty + * string. + */ + @Output() + searchChange: EventEmitter = new EventEmitter(); - /** Emitted when the search term is changed. The search term is provided - * in the 'value' property of the returned object. If the term is less - * than three characters in length then the term is truncated to an empty - * string. - */ - @Output() - searchChange: EventEmitter = new EventEmitter(); + /** Emitted when a file item from the list of "find-as-you-type" results is selected. */ + @Output() + optionClicked: EventEmitter = new EventEmitter(); - /** Emitted when a file item from the list of "find-as-you-type" results is selected. */ - @Output() - optionClicked: EventEmitter = new EventEmitter(); + @ViewChild('search') + searchAutocomplete: SearchComponent; - @ViewChild('search') - searchAutocomplete: SearchComponent; + @ViewChild('searchInput') + searchInput: ElementRef; - @ViewChild('searchInput') - searchInput: ElementRef; + @ViewChildren(MatListItem) + private listResultElement: QueryList; - @ViewChildren(MatListItem) - private listResultElement: QueryList; + @ContentChild(EmptySearchResultComponent) + emptySearchTemplate: EmptySearchResultComponent; - @ContentChild(EmptySearchResultComponent) - emptySearchTemplate: EmptySearchResultComponent; + searchTerm = ''; + subscriptAnimationState: string; + noSearchResultTemplate: TemplateRef = null; + skipToggle = false; + toggleDebounceTime = 200; - searchTerm = ''; - subscriptAnimationState: string; - noSearchResultTemplate: TemplateRef = null; - skipToggle = false; - toggleDebounceTime = 200; + private toggleSearch = new Subject(); + private focusSubject = new Subject(); - private toggleSearch = new Subject(); - private focusSubject = new Subject(); + constructor(private thumbnailService: ThumbnailService) { + this.toggleSearch + .asObservable() + .pipe(debounceTime(this.toggleDebounceTime)) + .subscribe(() => { + if (this.expandable && !this.skipToggle) { + this.subscriptAnimationState = + this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive'; - constructor(private thumbnailService: ThumbnailService) { - - this.toggleSearch.asObservable().pipe(debounceTime(this.toggleDebounceTime)).subscribe(() => { - if (this.expandable && !this.skipToggle) { - this.subscriptAnimationState = this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive'; - - if (this.subscriptAnimationState === 'inactive') { - this.searchTerm = ''; - this.searchAutocomplete.resetResults(); - if ( document.activeElement.id === this.searchInput.nativeElement.id) { - this.searchInput.nativeElement.blur(); - } - } + if (this.subscriptAnimationState === 'inactive') { + this.searchTerm = ''; + this.searchAutocomplete.resetResults(); + if ( + document.activeElement.id === this.searchInput.nativeElement.id + ) { + this.searchInput.nativeElement.blur(); } - this.skipToggle = false; - }); - } - - applySearchFocus(animationDoneEvent) { - if (animationDoneEvent.toState === 'active') { - this.searchInput.nativeElement.focus(); + } } + this.skipToggle = false; + }); + } + + applySearchFocus(animationDoneEvent) { + if (animationDoneEvent.toState === 'active') { + this.searchInput.nativeElement.focus(); + } + } + + ngOnInit() { + this.subscriptAnimationState = this.expandable + ? 'inactive' + : 'no-animation'; + this.setupFocusEventHandlers(); + } + + isNoSearchTemplatePresent(): boolean { + return this.emptySearchTemplate ? true : false; + } + + ngOnDestroy(): void { + if (this.focusSubject) { + this.focusSubject.unsubscribe(); + this.focusSubject = null; } - ngOnInit() { - this.subscriptAnimationState = this.expandable ? 'inactive' : 'no-animation'; - this.setupFocusEventHandlers(); + if (this.toggleSearch) { + this.toggleSearch.unsubscribe(); + this.toggleSearch = null; + } + } + + searchSubmit(event: any) { + this.submit.emit(event); + this.toggleSearchBar(); + } + + inputChange(event: any) { + this.searchChange.emit(event); + } + + getAutoComplete(): string { + return this.autocomplete ? 'on' : 'off'; + } + + getMimeTypeIcon(node: MinimalNodeEntity): string { + let mimeType; + + if (node.entry.content && node.entry.content.mimeType) { + mimeType = node.entry.content.mimeType; + } + if (node.entry.isFolder) { + mimeType = 'folder'; } - isNoSearchTemplatePresent(): boolean { - return this.emptySearchTemplate ? true : false; + return this.thumbnailService.getMimeTypeIcon(mimeType); + } + + isSearchBarActive() { + return this.subscriptAnimationState === 'active' && this.liveSearchEnabled; + } + + toggleSearchBar() { + if (this.toggleSearch) { + this.toggleSearch.next(); } + } - ngOnDestroy(): void { - if (this.focusSubject) { - this.focusSubject.unsubscribe(); - this.focusSubject = null; - } - - if (this.toggleSearch) { - this.toggleSearch.unsubscribe(); - this.toggleSearch = null; - } + elementClicked(item: any) { + if (item.entry) { + this.optionClicked.next(item); + this.toggleSearchBar(); } + } - searchSubmit(event: any) { - this.submit.emit(event); + onFocus($event): void { + this.focusSubject.next($event); + } + + onBlur($event): void { + this.focusSubject.next($event); + } + + activateToolbar() { + if (!this.isSearchBarActive()) { + this.toggleSearchBar(); + } + } + + selectFirstResult() { + if (this.listResultElement && this.listResultElement.length > 0) { + const firstElement: MatListItem = ( + this.listResultElement.first + ); + firstElement._getHostElement().focus(); + } + } + + onRowArrowDown($event: KeyboardEvent): void { + const nextElement: any = this.getNextElementSibling($event.target); + if (nextElement) { + nextElement.focus(); + } + } + + onRowArrowUp($event: KeyboardEvent): void { + const previousElement: any = this.getPreviousElementSibling(( + $event.target + )); + if (previousElement) { + previousElement.focus(); + } else { + this.searchInput.nativeElement.focus(); + this.focusSubject.next(new FocusEvent('focus')); + } + } + + private setupFocusEventHandlers() { + this.focusSubject + .pipe( + debounceTime(50), + filter(($event: any) => { + return ( + this.isSearchBarActive() && + ($event.type === 'blur' || $event.type === 'focusout') + ); + }) + ) + .subscribe(() => { this.toggleSearchBar(); - } + }); + } - inputChange(event: any) { - this.searchChange.emit(event); - } + clear(event: any) { + this.searchTerm = ''; + this.searchChange.emit(''); + this.skipToggle = true; + } - getAutoComplete(): string { - return this.autocomplete ? 'on' : 'off'; - } - - getMimeTypeIcon(node: MinimalNodeEntity): string { - let mimeType; - - if (node.entry.content && node.entry.content.mimeType) { - mimeType = node.entry.content.mimeType; - } - if (node.entry.isFolder) { - mimeType = 'folder'; - } - - return this.thumbnailService.getMimeTypeIcon(mimeType); - } - - isSearchBarActive() { - return this.subscriptAnimationState === 'active' && this.liveSearchEnabled; - } - - toggleSearchBar() { - if (this.toggleSearch) { - this.toggleSearch.next(); - } - } - - elementClicked(item: any) { - if (item.entry) { - this.optionClicked.next(item); - this.toggleSearchBar(); - } - } - - onFocus($event): void { - this.focusSubject.next($event); - } - - onBlur($event): void { - this.focusSubject.next($event); - } - - activateToolbar() { - if (!this.isSearchBarActive()) { - this.toggleSearchBar(); - } - } - - selectFirstResult() { - if ( this.listResultElement && this.listResultElement.length > 0) { - const firstElement: MatListItem = this.listResultElement.first; - firstElement._getHostElement().focus(); - } - } - - onRowArrowDown($event: KeyboardEvent): void { - const nextElement: any = this.getNextElementSibling( $event.target); - if (nextElement) { - nextElement.focus(); - } - } - - onRowArrowUp($event: KeyboardEvent): void { - const previousElement: any = this.getPreviousElementSibling( $event.target); - if (previousElement) { - previousElement.focus(); - } else { - this.searchInput.nativeElement.focus(); - this.focusSubject.next(new FocusEvent('focus')); - } - } - - private setupFocusEventHandlers() { - this.focusSubject.pipe( - debounceTime(50), - filter(($event: any) => { - return this.isSearchBarActive() && ($event.type === 'blur' || $event.type === 'focusout'); - }) - ).subscribe(() => { - this.toggleSearchBar(); - }); - } - - clear(event: any) { - this.searchTerm = ''; - this.searchChange.emit(''); - this.skipToggle = true; - } - - private getNextElementSibling(node: Element): Element { - return node.nextElementSibling; - } - - private getPreviousElementSibling(node: Element): Element { - return node.previousElementSibling; - } + private getNextElementSibling(node: Element): Element { + return node.nextElementSibling; + } + private getPreviousElementSibling(node: Element): Element { + return node.previousElementSibling; + } } diff --git a/src/app/components/search/search-input/search-input.component.spec.ts b/src/app/components/search/search-input/search-input.component.spec.ts index 59d5a0614..cfa35b5f8 100644 --- a/src/app/components/search/search-input/search-input.component.spec.ts +++ b/src/app/components/search/search-input/search-input.component.spec.ts @@ -24,65 +24,74 @@ */ import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing'; +import { + TestBed, + async, + ComponentFixture, + fakeAsync, + tick +} from '@angular/core/testing'; import { SearchInputComponent } from './search-input.component'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { Actions, ofType } from '@ngrx/effects'; -import { NAVIGATE_FOLDER, NavigateToFolder, VIEW_FILE, ViewFileAction } from '../../../store/actions'; +import { + NAVIGATE_FOLDER, + NavigateToFolder, + VIEW_FILE, + ViewFileAction +} from '../../../store/actions'; import { map } from 'rxjs/operators'; describe('SearchInputComponent', () => { - let fixture: ComponentFixture; - let component: SearchInputComponent; - let actions$: Actions; + let fixture: ComponentFixture; + let component: SearchInputComponent; + let actions$: Actions; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - AppTestingModule - ], - declarations: [ - SearchInputComponent - ], - schemas: [ NO_ERRORS_SCHEMA ] + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [SearchInputComponent], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents() + .then(() => { + actions$ = TestBed.get(Actions); + fixture = TestBed.createComponent(SearchInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + })); + + describe('onItemClicked()', () => { + it('opens preview if node is file', fakeAsync(done => { + actions$.pipe( + ofType(VIEW_FILE), + map(action => { + expect(action.payload.entry.id).toBe('node-id'); + done(); }) - .compileComponents() - .then(() => { - actions$ = TestBed.get(Actions); - fixture = TestBed.createComponent(SearchInputComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + ); + + const node = { + entry: { isFile: true, id: 'node-id', parentId: 'parent-id' } + }; + + component.onItemClicked(node); + tick(); })); - describe('onItemClicked()', () => { - it('opens preview if node is file', fakeAsync(done => { - actions$.pipe( - ofType(VIEW_FILE), - map(action => { - expect(action.payload.entry.id).toBe('node-id'); - done(); - }) - ); - - const node = { entry: { isFile: true, id: 'node-id', parentId: 'parent-id' } }; - - component.onItemClicked(node); - tick(); - })); - - it('navigates if node is folder', fakeAsync(done => { - actions$.pipe( - ofType(NAVIGATE_FOLDER), - map(action => { - expect(action.payload.entry.id).toBe('folder-id'); - done(); - }) - ); - const node = { entry: { id: 'folder-id', isFolder: true } }; - component.onItemClicked(node); - tick(); - })); - }); + it('navigates if node is folder', fakeAsync(done => { + actions$.pipe( + ofType(NAVIGATE_FOLDER), + map(action => { + expect(action.payload.entry.id).toBe('folder-id'); + done(); + }) + ); + const node = { entry: { id: 'folder-id', isFolder: true } }; + component.onItemClicked(node); + tick(); + })); + }); }); diff --git a/src/app/components/search/search-input/search-input.component.theme.scss b/src/app/components/search/search-input/search-input.component.theme.scss index afcbd6b76..36a9249b0 100644 --- a/src/app/components/search/search-input/search-input.component.theme.scss +++ b/src/app/components/search/search-input/search-input.component.theme.scss @@ -1,37 +1,37 @@ @mixin aca-search-input-theme($theme) { - $background: map-get($theme, background); + $background: map-get($theme, background); - .aca-search-input{ - display: flex; - box-sizing: border-box; - padding: 0; - flex-direction: row; - align-items: center; - white-space: nowrap; + .aca-search-input { + display: flex; + box-sizing: border-box; + padding: 0; + flex-direction: row; + align-items: center; + white-space: nowrap; - .adf-search-control { - color: mat-color($background, card); + .adf-search-control { + color: mat-color($background, card); - .mat-form-field-underline { - background-color: mat-color($background, card); - } + .mat-form-field-underline { + background-color: mat-color($background, card); + } - .adf-input-form-field-divider { - font-size: 14px; - } - } - - .adf-search-button.mat-icon-button { - left: -15px; - margin-left: 15px; - align-items: flex-start; - font: 400 11px system-ui; - color: mat-color($background, card); - - .mat-icon { - font-size: 24px; - padding-right: 0; - } - } + .adf-input-form-field-divider { + font-size: 14px; + } } + + .adf-search-button.mat-icon-button { + left: -15px; + margin-left: 15px; + align-items: flex-start; + font: 400 11px system-ui; + color: mat-color($background, card); + + .mat-icon { + font-size: 24px; + padding-right: 0; + } + } + } } diff --git a/src/app/components/search/search-input/search-input.component.ts b/src/app/components/search/search-input/search-input.component.ts index f4045bd81..379715ac3 100644 --- a/src/app/components/search/search-input/search-input.component.ts +++ b/src/app/components/search/search-input/search-input.component.ts @@ -25,127 +25,132 @@ import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { - NavigationEnd, PRIMARY_OUTLET, Router, RouterEvent, UrlSegment, UrlSegmentGroup, - UrlTree + NavigationEnd, + PRIMARY_OUTLET, + Router, + RouterEvent, + UrlSegment, + UrlSegmentGroup, + UrlTree } from '@angular/router'; import { MinimalNodeEntity } from 'alfresco-js-api'; import { SearchInputControlComponent } from '../search-input-control/search-input-control.component'; import { Store } from '@ngrx/store'; import { AppStore } from '../../../store/states/app.state'; -import { SearchByTermAction, NavigateToFolder, ViewFileAction } from '../../../store/actions'; +import { + SearchByTermAction, + NavigateToFolder, + ViewFileAction +} from '../../../store/actions'; import { filter } from 'rxjs/operators'; @Component({ - selector: 'aca-search-input', - templateUrl: 'search-input.component.html', - encapsulation: ViewEncapsulation.None, - host: { class: 'aca-search-input' } + selector: 'aca-search-input', + templateUrl: 'search-input.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'aca-search-input' } }) export class SearchInputComponent implements OnInit { + hasOneChange = false; + hasNewChange = false; + navigationTimer: any; + enableLiveSearch = true; - hasOneChange = false; - hasNewChange = false; - navigationTimer: any; - enableLiveSearch = true; + @ViewChild('searchInputControl') + searchInputControl: SearchInputControlComponent; - @ViewChild('searchInputControl') - searchInputControl: SearchInputControlComponent; + constructor(private router: Router, private store: Store) {} - constructor(private router: Router, private store: Store) { - } + ngOnInit() { + this.showInputValue(); - ngOnInit() { - this.showInputValue(); - - this.router.events - .pipe(filter(e => e instanceof RouterEvent)) - .subscribe(event => { - if (event instanceof NavigationEnd) { - this.showInputValue(); - } - }); - } - - showInputValue() { - if (this.onSearchResults) { - - let searchedWord = null; - const urlTree: UrlTree = this.router.parseUrl(this.router.url); - const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET]; - - if (urlSegmentGroup) { - const urlSegments: UrlSegment[] = urlSegmentGroup.segments; - searchedWord = urlSegments[0].parameters['q']; - } - - if (this.searchInputControl) { - this.enableLiveSearch = false; - this.searchInputControl.searchTerm = searchedWord; - this.searchInputControl.subscriptAnimationState = 'no-animation'; - } - - } else { - if (this.searchInputControl.subscriptAnimationState === 'no-animation') { - this.searchInputControl.subscriptAnimationState = 'active'; - this.searchInputControl.searchTerm = ''; - this.searchInputControl.toggleSearchBar(); - } - - if (!this.enableLiveSearch) { - setTimeout(() => { - this.enableLiveSearch = true; - }, this.searchInputControl.toggleDebounceTime + 100); - } + this.router.events + .pipe(filter(e => e instanceof RouterEvent)) + .subscribe(event => { + if (event instanceof NavigationEnd) { + this.showInputValue(); } - } + }); + } - onItemClicked(node: MinimalNodeEntity) { - if (node && node.entry) { - const { isFile, isFolder } = node.entry; - if (isFile) { - this.store.dispatch(new ViewFileAction(node)); - } else if (isFolder) { - this.store.dispatch(new NavigateToFolder(node)); - } - } - } + showInputValue() { + if (this.onSearchResults) { + let searchedWord = null; + const urlTree: UrlTree = this.router.parseUrl(this.router.url); + const urlSegmentGroup: UrlSegmentGroup = + urlTree.root.children[PRIMARY_OUTLET]; - /** - * Called when the user submits the search, e.g. hits enter or clicks submit - * - * @param event Parameters relating to the search - */ - onSearchSubmit(event: KeyboardEvent) { - const searchTerm = (event.target as HTMLInputElement).value; + if (urlSegmentGroup) { + const urlSegments: UrlSegment[] = urlSegmentGroup.segments; + searchedWord = urlSegments[0].parameters['q']; + } + + if (this.searchInputControl) { + this.enableLiveSearch = false; + this.searchInputControl.searchTerm = searchedWord; + this.searchInputControl.subscriptAnimationState = 'no-animation'; + } + } else { + if (this.searchInputControl.subscriptAnimationState === 'no-animation') { + this.searchInputControl.subscriptAnimationState = 'active'; + this.searchInputControl.searchTerm = ''; + this.searchInputControl.toggleSearchBar(); + } + + if (!this.enableLiveSearch) { + setTimeout(() => { + this.enableLiveSearch = true; + }, this.searchInputControl.toggleDebounceTime + 100); + } + } + } + + onItemClicked(node: MinimalNodeEntity) { + if (node && node.entry) { + const { isFile, isFolder } = node.entry; + if (isFile) { + this.store.dispatch(new ViewFileAction(node)); + } else if (isFolder) { + this.store.dispatch(new NavigateToFolder(node)); + } + } + } + + /** + * Called when the user submits the search, e.g. hits enter or clicks submit + * + * @param event Parameters relating to the search + */ + onSearchSubmit(event: KeyboardEvent) { + const searchTerm = (event.target as HTMLInputElement).value; + if (searchTerm) { + this.store.dispatch(new SearchByTermAction(searchTerm)); + } + } + + onSearchChange(searchTerm: string) { + if (this.onSearchResults) { + if (this.hasOneChange) { + this.hasNewChange = true; + } else { + this.hasOneChange = true; + } + + if (this.hasNewChange) { + clearTimeout(this.navigationTimer); + this.hasNewChange = false; + } + + this.navigationTimer = setTimeout(() => { if (searchTerm) { - this.store.dispatch(new SearchByTermAction(searchTerm)); + this.store.dispatch(new SearchByTermAction(searchTerm)); } + this.hasOneChange = false; + }, 1000); } + } - onSearchChange(searchTerm: string) { - if (this.onSearchResults) { - - if (this.hasOneChange) { - this.hasNewChange = true; - } else { - this.hasOneChange = true; - } - - if (this.hasNewChange) { - clearTimeout(this.navigationTimer); - this.hasNewChange = false; - } - - this.navigationTimer = setTimeout(() => { - if (searchTerm) { - this.store.dispatch(new SearchByTermAction(searchTerm)); - } - this.hasOneChange = false; - }, 1000); - } - } - - get onSearchResults() { - return this.router.url.indexOf('/search') === 0; - } + get onSearchResults() { + return this.router.url.indexOf('/search') === 0; + } } diff --git a/src/app/components/search/search-results-row/search-results-row.component.scss b/src/app/components/search/search-results-row/search-results-row.component.scss index 0ccc31395..9004d8b6e 100644 --- a/src/app/components/search/search-results-row/search-results-row.component.scss +++ b/src/app/components/search/search-results-row/search-results-row.component.scss @@ -1,24 +1,24 @@ @import 'mixins'; .aca-search-results-row { - @include flex-column; + @include flex-column; } .line { - margin: 5px 0; + margin: 5px 0; } .bold { - font-weight: 400; - color: rgba(0, 0, 0, 0.87); + font-weight: 400; + color: rgba(0, 0, 0, 0.87); } .link { - text-decoration: none; - color: rgba(0, 0, 0, 0.87); + text-decoration: none; + color: rgba(0, 0, 0, 0.87); } .link:hover { - color: #2196F3; - text-decoration: underline; + color: #2196f3; + text-decoration: underline; } diff --git a/src/app/components/search/search-results-row/search-results-row.component.ts b/src/app/components/search/search-results-row/search-results-row.component.ts index 8c346b802..4085a5b0e 100644 --- a/src/app/components/search/search-results-row/search-results-row.component.ts +++ b/src/app/components/search/search-results-row/search-results-row.component.ts @@ -23,7 +23,13 @@ * along with Alfresco. If not, see . */ -import { Component, Input, OnInit, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core'; +import { + Component, + Input, + OnInit, + ViewEncapsulation, + ChangeDetectionStrategy +} from '@angular/core'; import { MinimalNodeEntity } from 'alfresco-js-api'; import { ViewFileAction } from '../../../store/actions'; import { Store } from '@ngrx/store'; @@ -31,85 +37,84 @@ import { AppStore } from '../../../store/states/app.state'; import { NavigateToFolder } from '../../../store/actions'; @Component({ - selector: 'aca-search-results-row', - templateUrl: './search-results-row.component.html', - styleUrls: ['./search-results-row.component.scss'], - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'aca-search-results-row' } + selector: 'aca-search-results-row', + templateUrl: './search-results-row.component.html', + styleUrls: ['./search-results-row.component.scss'], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'aca-search-results-row' } }) export class SearchResultsRowComponent implements OnInit { - private node: MinimalNodeEntity; + private node: MinimalNodeEntity; - @Input() context: any; + @Input() + context: any; - constructor(private store: Store) {} + constructor(private store: Store) {} - ngOnInit() { - this.node = this.context.row.node; - } + ngOnInit() { + this.node = this.context.row.node; + } - get name() { - return this.getValue('name'); - } + get name() { + return this.getValue('name'); + } - get title() { - return this.getValue('properties["cm:title"]'); - } + get title() { + return this.getValue('properties["cm:title"]'); + } - get description() { - return this.getValue('properties["cm:description"]'); - } + get description() { + return this.getValue('properties["cm:description"]'); + } - get modifiedAt() { - return this.getValue('modifiedAt'); - } + get modifiedAt() { + return this.getValue('modifiedAt'); + } - get size() { - return this.getValue('content.sizeInBytes'); - } + get size() { + return this.getValue('content.sizeInBytes'); + } - get user() { - return this.getValue('modifiedByUser.displayName'); - } + get user() { + return this.getValue('modifiedByUser.displayName'); + } - get hasDescription() { - return this.description; - } + get hasDescription() { + return this.description; + } - get hasTitle() { - return this.title; - } + get hasTitle() { + return this.title; + } - get showTitle() { - return this.name !== this.title; - } + get showTitle() { + return this.name !== this.title; + } - get hasSize() { - return this.size; - } + get hasSize() { + return this.size; + } - get isFile() { - return this.getValue('isFile'); - } + get isFile() { + return this.getValue('isFile'); + } - showPreview() { - this.store.dispatch( - new ViewFileAction(this.node) - ); - } + showPreview() { + this.store.dispatch(new ViewFileAction(this.node)); + } - navigate() { - this.store.dispatch(new NavigateToFolder(this.node)); - } + navigate() { + this.store.dispatch(new NavigateToFolder(this.node)); + } - private getValue(path) { - return path - .replace('["', '.') - .replace('"]', '') - .replace('[', '.') - .replace(']', '') - .split('.') - .reduce((acc, part) => (acc ? acc[part] : null), this.node.entry); - } + private getValue(path) { + return path + .replace('["', '.') + .replace('"]', '') + .replace('[', '.') + .replace(']', '') + .split('.') + .reduce((acc, part) => (acc ? acc[part] : null), this.node.entry); + } } diff --git a/src/app/components/search/search-results/search-results.component.scss b/src/app/components/search/search-results/search-results.component.scss index 6a201a3b1..271984e08 100644 --- a/src/app/components/search/search-results/search-results.component.scss +++ b/src/app/components/search/search-results/search-results.component.scss @@ -1,60 +1,60 @@ @import 'mixins'; .adf-search-results { + @include flex-row; + + &__facets { + display: flex; + flex-direction: row; + margin-top: 5px; + margin-bottom: 5px; + } + + &__content { + @include flex-column; + border-left: 1px solid #eee; + } + + &__content-header { + display: flex; + padding: 0 25px 0 25px; + flex-direction: row; + align-items: center; + border-bottom: 1px solid #eee; + } + + &--info-text { + flex: 1; + font-size: 16px; + color: rgba(0, 0, 0, 0.54); + } + + .adf-search-filter { + min-width: 260px; + padding: 5px; + height: 100%; + overflow: scroll; + + &--hidden { + display: none; + } + } + + .text--bold { + font-weight: 600; + } + + .content { @include flex-row; + flex: unset; + height: unset; + padding-top: 8px; + padding-bottom: 8px; + flex-wrap: wrap; - &__facets { - display: flex; - flex-direction: row; - margin-top: 5px; - margin-bottom: 5px; - } - - &__content { - @include flex-column; - border-left: 1px solid #eee; - } - - &__content-header { - display: flex; - padding: 0 25px 0 25px; - flex-direction: row; - align-items: center; - border-bottom: 1px solid #eee; - } - - &--info-text { - flex: 1; - font-size: 16px; - color: rgba(0, 0, 0, 0.54); - } - - .adf-search-filter { - min-width: 260px; - padding: 5px; - height: 100%; - overflow: scroll; - - &--hidden { - display: none; - } - } - - .text--bold { - font-weight: 600; - } - - .content { - @include flex-row; - flex: unset; - height: unset; - padding-top: 8px; - padding-bottom: 8px; - flex-wrap: wrap; - - &__side--left { - @include flex-column; - height: unset; - } + &__side--left { + @include flex-column; + height: unset; } + } } diff --git a/src/app/components/search/search-results/search-results.component.spec.ts b/src/app/components/search/search-results/search-results.component.spec.ts index 4c4f369f9..55f543f77 100644 --- a/src/app/components/search/search-results/search-results.component.spec.ts +++ b/src/app/components/search/search-results/search-results.component.spec.ts @@ -8,9 +8,8 @@ describe('SearchComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ SearchResultsComponent ] - }) - .compileComponents(); + declarations: [SearchResultsComponent] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/app/components/search/search-results/search-results.component.ts b/src/app/components/search/search-results/search-results.component.ts index 9bd8362fd..100afce26 100644 --- a/src/app/components/search/search-results/search-results.component.ts +++ b/src/app/components/search/search-results/search-results.component.ts @@ -26,7 +26,11 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { NodePaging, Pagination, MinimalNodeEntity } from 'alfresco-js-api'; import { ActivatedRoute, Params } from '@angular/router'; -import { SearchQueryBuilderService, SearchComponent as AdfSearchComponent, SearchFilterComponent } from '@alfresco/adf-content-services'; +import { + SearchQueryBuilderService, + SearchComponent as AdfSearchComponent, + SearchFilterComponent +} from '@alfresco/adf-content-services'; import { PageComponent } from '../../page.component'; import { Store } from '@ngrx/store'; import { AppStore } from '../../../store/states/app.state'; @@ -41,140 +45,146 @@ import { ContentManagementService } from '../../../services/content-management.s providers: [SearchQueryBuilderService] }) export class SearchResultsComponent extends PageComponent implements OnInit { + @ViewChild('search') + search: AdfSearchComponent; - @ViewChild('search') - search: AdfSearchComponent; + @ViewChild('searchFilter') + searchFilter: SearchFilterComponent; - @ViewChild('searchFilter') - searchFilter: SearchFilterComponent; + searchedWord: string; + queryParamName = 'q'; + data: NodePaging; + totalResults = 0; + hasSelectedFilters = false; + sorting = ['name', 'asc']; + isLoading = false; - searchedWord: string; - queryParamName = 'q'; - data: NodePaging; - totalResults = 0; - hasSelectedFilters = false; - sorting = ['name', 'asc']; - isLoading = false; + constructor( + private queryBuilder: SearchQueryBuilderService, + private route: ActivatedRoute, + store: Store, + extensions: AppExtensionService, + content: ContentManagementService + ) { + super(store, extensions, content); - constructor( - private queryBuilder: SearchQueryBuilderService, - private route: ActivatedRoute, - store: Store, - extensions: AppExtensionService, - content: ContentManagementService - ) { - super(store, extensions, content); + queryBuilder.paging = { + skipCount: 0, + maxItems: 25 + }; + } - queryBuilder.paging = { - skipCount: 0, - maxItems: 25 - }; - } + ngOnInit() { + super.ngOnInit(); - ngOnInit() { - super.ngOnInit(); + this.sorting = this.getSorting(); + this.subscriptions.push( + this.queryBuilder.updated.subscribe(() => { this.sorting = this.getSorting(); + this.isLoading = true; + }), - this.subscriptions.push( - this.queryBuilder.updated.subscribe(() => { - this.sorting = this.getSorting(); - this.isLoading = true; - }), + this.queryBuilder.executed.subscribe(data => { + this.onSearchResultLoaded(data); + this.isLoading = false; + }) + ); - this.queryBuilder.executed.subscribe(data => { - this.onSearchResultLoaded(data); - this.isLoading = false; - }) - ); + if (this.route) { + this.route.params.forEach((params: Params) => { + this.searchedWord = params.hasOwnProperty(this.queryParamName) + ? params[this.queryParamName] + : null; + const query = this.formatSearchQuery(this.searchedWord); - if (this.route) { - this.route.params.forEach((params: Params) => { - this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null; - const query = this.formatSearchQuery(this.searchedWord); - - if (query) { - this.queryBuilder.userQuery = query; - this.queryBuilder.update(); - } else { - this.queryBuilder.userQuery = null; - this.queryBuilder.executed.next( {list: { pagination: { totalItems: 0 }, entries: []}} ); - } - }); + if (query) { + this.queryBuilder.userQuery = query; + this.queryBuilder.update(); + } else { + this.queryBuilder.userQuery = null; + this.queryBuilder.executed.next({ + list: { pagination: { totalItems: 0 }, entries: [] } + }); } + }); + } + } + + private formatSearchQuery(userInput: string) { + if (!userInput) { + return null; } - private formatSearchQuery(userInput: string) { - if (!userInput) { - return null; - } + const suffix = userInput.lastIndexOf('*') >= 0 ? '' : '*'; + const query = `${userInput}${suffix} OR name:${userInput}${suffix}`; - const suffix = userInput.lastIndexOf('*') >= 0 ? '' : '*'; - const query = `${userInput}${suffix} OR name:${userInput}${suffix}`; + return query; + } - return query; + onSearchResultLoaded(nodePaging: NodePaging) { + this.data = nodePaging; + this.totalResults = this.getNumberOfResults(); + this.hasSelectedFilters = this.isFiltered(); + } + + getNumberOfResults() { + if (this.data && this.data.list && this.data.list.pagination) { + return this.data.list.pagination.totalItems; + } + return 0; + } + + isFiltered(): boolean { + return ( + this.searchFilter.selectedFacetQueries.length > 0 || + this.searchFilter.selectedBuckets.length > 0 || + this.hasCheckedCategories() + ); + } + + hasCheckedCategories() { + const checkedCategory = this.queryBuilder.categories.find( + category => !!this.queryBuilder.queryFragments[category.id] + ); + return !!checkedCategory; + } + + onPaginationChanged(pagination: Pagination) { + this.queryBuilder.paging = { + maxItems: pagination.maxItems, + skipCount: pagination.skipCount + }; + this.queryBuilder.update(); + } + + private getSorting(): string[] { + const primary = this.queryBuilder.getPrimarySorting(); + + if (primary) { + return [primary.key, primary.ascending ? 'asc' : 'desc']; } - onSearchResultLoaded(nodePaging: NodePaging) { - this.data = nodePaging; - this.totalResults = this.getNumberOfResults(); - this.hasSelectedFilters = this.isFiltered(); + return ['name', 'asc']; + } + + onNodeDoubleClick(node: MinimalNodeEntity) { + if (node && node.entry) { + if (node.entry.isFolder) { + this.store.dispatch(new NavigateToFolder(node)); + return; + } + + if (PageComponent.isLockedNode(node.entry)) { + event.preventDefault(); + return; + } + + this.showPreview(node); } + } - getNumberOfResults() { - if (this.data && this.data.list && this.data.list.pagination) { - return this.data.list.pagination.totalItems; - } - return 0; - } - - isFiltered(): boolean { - return this.searchFilter.selectedFacetQueries.length > 0 - || this.searchFilter.selectedBuckets.length > 0 - || this.hasCheckedCategories(); - } - - hasCheckedCategories() { - const checkedCategory = this.queryBuilder.categories - .find(category => !!this.queryBuilder.queryFragments[category.id]); - return !!checkedCategory; - } - - onPaginationChanged(pagination: Pagination) { - this.queryBuilder.paging = { - maxItems: pagination.maxItems, - skipCount: pagination.skipCount - }; - this.queryBuilder.update(); - } - - private getSorting(): string[] { - const primary = this.queryBuilder.getPrimarySorting(); - - if (primary) { - return [primary.key, primary.ascending ? 'asc' : 'desc']; - } - - return ['name', 'asc']; - } - - onNodeDoubleClick(node: MinimalNodeEntity) { - if (node && node.entry) { - if (node.entry.isFolder) { - this.store.dispatch(new NavigateToFolder(node)); - return; - } - - if (PageComponent.isLockedNode(node.entry)) { - event.preventDefault(); - return; - } - - this.showPreview(node); - } - } - - hideSearchFilter() { - return !this.totalResults && !this.hasSelectedFilters; - } + hideSearchFilter() { + return !this.totalResults && !this.hasSelectedFilters; + } } diff --git a/src/app/components/settings/settings.component.theme.scss b/src/app/components/settings/settings.component.theme.scss index 11de98a19..a96d55ab6 100644 --- a/src/app/components/settings/settings.component.theme.scss +++ b/src/app/components/settings/settings.component.theme.scss @@ -1,70 +1,70 @@ @mixin aca-settings-theme($theme) { - $background: map-get($theme, background); - $app-menu-height: 64px; + $background: map-get($theme, background); + $app-menu-height: 64px; - .aca-settings { - .settings-input { - width: 50%; - } + .aca-settings { + .settings-input { + width: 50%; + } - .settings-buttons { - text-align: right; + .settings-buttons { + text-align: right; - .mat-button { - text-transform: uppercase; - } - } + .mat-button { + text-transform: uppercase; + } + } - .app-menu { + .app-menu { + height: $app-menu-height; + + &.adf-toolbar { + .mat-toolbar { + background-color: inherit; + font-family: inherit; + min-height: $app-menu-height; + height: $app-menu-height; + + .mat-toolbar-layout { height: $app-menu-height; - &.adf-toolbar { - .mat-toolbar { - background-color: inherit; - font-family: inherit; - min-height: $app-menu-height; - height: $app-menu-height; - - .mat-toolbar-layout { - height: $app-menu-height; - - .mat-toolbar-row { - height: $app-menu-height; - } - } - } - - .adf-toolbar-divider { - margin-left: 5px; - margin-right: 5px; - - & > div { - background-color: mat-color($background, card); - } - } - - .adf-toolbar-title { - color: mat-color($background, card); - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - } - } - - .app-menu__title { - width: 100px; - height: 50px; - margin-left: 40px; - display: flex; - justify-content: center; - align-items: stretch; - - &> img { - width: 100%; - object-fit: contain; - } + .mat-toolbar-row { + height: $app-menu-height; } + } } + + .adf-toolbar-divider { + margin-left: 5px; + margin-right: 5px; + + & > div { + background-color: mat-color($background, card); + } + } + + .adf-toolbar-title { + color: mat-color($background, card); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + } + } + + .app-menu__title { + width: 100px; + height: 50px; + margin-left: 40px; + display: flex; + justify-content: center; + align-items: stretch; + + & > img { + width: 100%; + object-fit: contain; + } + } } + } } diff --git a/src/app/components/settings/settings.component.ts b/src/app/components/settings/settings.component.ts index 419fd185a..70155f0b7 100644 --- a/src/app/components/settings/settings.component.ts +++ b/src/app/components/settings/settings.component.ts @@ -24,86 +24,98 @@ */ import { Component, ViewEncapsulation, OnInit } from '@angular/core'; -import { AppConfigService, StorageService, SettingsService } from '@alfresco/adf-core'; +import { + AppConfigService, + StorageService, + SettingsService +} from '@alfresco/adf-core'; import { Validators, FormGroup, FormBuilder } from '@angular/forms'; import { Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { AppStore } from '../../store/states'; -import { appLanguagePicker, selectHeaderColor, selectAppName, selectUser } from '../../store/selectors/app.selectors'; +import { + appLanguagePicker, + selectHeaderColor, + selectAppName, + selectUser +} from '../../store/selectors/app.selectors'; import { MatCheckboxChange } from '@angular/material'; import { SetLanguagePickerAction } from '../../store/actions'; import { ProfileState } from '@alfresco/adf-extensions'; @Component({ - selector: 'aca-settings', - templateUrl: './settings.component.html', - encapsulation: ViewEncapsulation.None, - host: { class: 'aca-settings' } + selector: 'aca-settings', + templateUrl: './settings.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'aca-settings' } }) export class SettingsComponent implements OnInit { + private defaultPath = '/assets/images/alfresco-logo-white.svg'; - private defaultPath = '/assets/images/alfresco-logo-white.svg'; + form: FormGroup; - form: FormGroup; + profile$: Observable; + appName$: Observable; + headerColor$: Observable; + languagePicker$: Observable; + experimental: Array<{ key: string; value: boolean }> = []; - profile$: Observable; - appName$: Observable; - headerColor$: Observable; - languagePicker$: Observable; - experimental: Array<{ key: string, value: boolean }> = []; + constructor( + private store: Store, + private appConfig: AppConfigService, + private settingsService: SettingsService, + private storage: StorageService, + private fb: FormBuilder + ) { + this.profile$ = store.select(selectUser); + this.appName$ = store.select(selectAppName); + this.languagePicker$ = store.select(appLanguagePicker); + this.headerColor$ = store.select(selectHeaderColor); + } - constructor( - private store: Store, - private appConfig: AppConfigService, - private settingsService: SettingsService, - private storage: StorageService, - private fb: FormBuilder) { - this.profile$ = store.select(selectUser); - this.appName$ = store.select(selectAppName); - this.languagePicker$ = store.select(appLanguagePicker); - this.headerColor$ = store.select(selectHeaderColor); - } + get logo() { + return this.appConfig.get('application.logo', this.defaultPath); + } - get logo() { - return this.appConfig.get('application.logo', this.defaultPath); + ngOnInit() { + this.form = this.fb.group({ + ecmHost: [ + '', + [Validators.required, Validators.pattern('^(http|https)://.*[^/]$')] + ] + }); + + this.reset(); + + const settings = this.appConfig.get('experimental'); + this.experimental = Object.keys(settings).map(key => { + const value = this.appConfig.get(`experimental.${key}`); + return { + key, + value: value === true || value === 'true' + }; + }); + } + + apply(model: any, isValid: boolean) { + if (isValid) { + this.storage.setItem('ecmHost', model.ecmHost); + // window.location.reload(true); } + } - ngOnInit() { - this.form = this.fb.group({ - ecmHost: ['', [Validators.required, Validators.pattern('^(http|https):\/\/.*[^/]$')]] - }); + reset() { + this.form.reset({ + ecmHost: this.storage.getItem('ecmHost') || this.settingsService.ecmHost + }); + } - this.reset(); + onLanguagePickerValueChanged(event: MatCheckboxChange) { + this.storage.setItem('languagePicker', event.checked.toString()); + this.store.dispatch(new SetLanguagePickerAction(event.checked)); + } - const settings = this.appConfig.get('experimental'); - this.experimental = Object.keys(settings).map(key => { - const value = this.appConfig.get(`experimental.${key}`); - return { - key, - value: (value === true || value === 'true') - }; - }); - } - - apply(model: any, isValid: boolean) { - if (isValid) { - this.storage.setItem('ecmHost', model.ecmHost); - // window.location.reload(true); - } - } - - reset() { - this.form.reset({ - ecmHost: this.storage.getItem('ecmHost') || this.settingsService.ecmHost - }); - } - - onLanguagePickerValueChanged(event: MatCheckboxChange) { - this.storage.setItem('languagePicker', event.checked.toString()); - this.store.dispatch(new SetLanguagePickerAction(event.checked)); - } - - onToggleExperimentalFeature(key: string, event: MatCheckboxChange) { - this.storage.setItem(`experimental.${key}`, event.checked.toString()); - } + onToggleExperimentalFeature(key: string, event: MatCheckboxChange) { + this.storage.setItem(`experimental.${key}`, event.checked.toString()); + } } diff --git a/src/app/components/settings/settings.module.ts b/src/app/components/settings/settings.module.ts index 884bb297b..84e8dbd80 100644 --- a/src/app/components/settings/settings.module.ts +++ b/src/app/components/settings/settings.module.ts @@ -30,18 +30,14 @@ import { CommonModule } from '@angular/common'; import { CoreModule } from '@alfresco/adf-core'; const routes: Routes = [ - { - path: '', - component: SettingsComponent - } + { + path: '', + component: SettingsComponent + } ]; @NgModule({ - imports: [ - CommonModule, - CoreModule.forChild(), - RouterModule.forChild(routes) - ], - declarations: [SettingsComponent] + imports: [CommonModule, CoreModule.forChild(), RouterModule.forChild(routes)], + declarations: [SettingsComponent] }) export class AppSettingsModule {} diff --git a/src/app/components/shared-files/shared-files.component.spec.ts b/src/app/components/shared-files/shared-files.component.spec.ts index d59f6448f..058d550ba 100644 --- a/src/app/components/shared-files/shared-files.component.spec.ts +++ b/src/app/components/shared-files/shared-files.component.spec.ts @@ -26,8 +26,12 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { - AlfrescoApiService, - TimeAgoPipe, NodeNameTooltipPipe, NodeFavoriteDirective, DataTableComponent, AppConfigPipe + AlfrescoApiService, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DataTableComponent, + AppConfigPipe } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../services/content-management.service'; @@ -36,86 +40,87 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { ExperimentalDirective } from '../../directives/experimental.directive'; describe('SharedFilesComponent', () => { - let fixture: ComponentFixture; - let component: SharedFilesComponent; - let contentService: ContentManagementService; - let alfrescoApi: AlfrescoApiService; - let page; + let fixture: ComponentFixture; + let component: SharedFilesComponent; + let contentService: ContentManagementService; + let alfrescoApi: AlfrescoApiService; + let page; + beforeEach(() => { + page = { + list: { + entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }], + pagination: { data: 'data' } + } + }; + }); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule], + declarations: [ + DataTableComponent, + TimeAgoPipe, + NodeNameTooltipPipe, + NodeFavoriteDirective, + DocumentListComponent, + SharedFilesComponent, + AppConfigPipe, + ExperimentalDirective + ], + schemas: [NO_ERRORS_SCHEMA] + }); + + fixture = TestBed.createComponent(SharedFilesComponent); + component = fixture.componentInstance; + + contentService = TestBed.get(ContentManagementService); + alfrescoApi = TestBed.get(AlfrescoApiService); + alfrescoApi.reset(); + + spyOn(alfrescoApi.sharedLinksApi, 'findSharedLinks').and.returnValue( + Promise.resolve(page) + ); + }); + + describe('OnInit', () => { beforeEach(() => { - page = { - list: { - entries: [ { entry: { id: 1 } }, { entry: { id: 2 } } ], - pagination: { data: 'data'} - } - }; + spyOn(component, 'reload').and.callFake(val => val); }); - beforeEach(() => { - TestBed - .configureTestingModule({ - imports: [ AppTestingModule ], - declarations: [ - DataTableComponent, - TimeAgoPipe, - NodeNameTooltipPipe, - NodeFavoriteDirective, - DocumentListComponent, - SharedFilesComponent, - AppConfigPipe, - ExperimentalDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }); + it('should refresh on deleteNode event', () => { + fixture.detectChanges(); - fixture = TestBed.createComponent(SharedFilesComponent); - component = fixture.componentInstance; + contentService.nodesDeleted.next(); - contentService = TestBed.get(ContentManagementService); - alfrescoApi = TestBed.get(AlfrescoApiService); - alfrescoApi.reset(); - - spyOn(alfrescoApi.sharedLinksApi, 'findSharedLinks').and.returnValue(Promise.resolve(page)); + expect(component.reload).toHaveBeenCalled(); }); - describe('OnInit', () => { - beforeEach(() => { - spyOn(component, 'reload').and.callFake(val => val); - }); + it('should refresh on restoreNode event', () => { + fixture.detectChanges(); - it('should refresh on deleteNode event', () => { - fixture.detectChanges(); + contentService.nodesRestored.next(); - contentService.nodesDeleted.next(); - - expect(component.reload).toHaveBeenCalled(); - }); - - it('should refresh on restoreNode event', () => { - fixture.detectChanges(); - - contentService.nodesRestored.next(); - - expect(component.reload).toHaveBeenCalled(); - }); - - it('should reload on move node event', () => { - fixture.detectChanges(); - - contentService.nodesMoved.next(); - - expect(component.reload).toHaveBeenCalled(); - }); + expect(component.reload).toHaveBeenCalled(); }); - describe('refresh', () => { - it('should call document list reload', () => { - spyOn(component.documentList, 'reload'); - fixture.detectChanges(); + it('should reload on move node event', () => { + fixture.detectChanges(); - component.reload(); + contentService.nodesMoved.next(); - expect(component.documentList.reload).toHaveBeenCalled(); - }); + expect(component.reload).toHaveBeenCalled(); }); + }); + + describe('refresh', () => { + it('should call document list reload', () => { + spyOn(component.documentList, 'reload'); + fixture.detectChanges(); + + component.reload(); + + expect(component.documentList.reload).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/components/shared-files/shared-files.component.ts b/src/app/components/shared-files/shared-files.component.ts index f81725596..ab2c3835f 100644 --- a/src/app/components/shared-files/shared-files.component.ts +++ b/src/app/components/shared-files/shared-files.component.ts @@ -32,37 +32,34 @@ import { AppStore } from '../../store/states/app.state'; import { AppExtensionService } from '../../extensions/extension.service'; @Component({ - templateUrl: './shared-files.component.html' + templateUrl: './shared-files.component.html' }) export class SharedFilesComponent extends PageComponent implements OnInit { - isSmallScreen = false; + isSmallScreen = false; - constructor( - store: Store, - extensions: AppExtensionService, - content: ContentManagementService, - private breakpointObserver: BreakpointObserver - ) { - super(store, extensions, content); - } + constructor( + store: Store, + extensions: AppExtensionService, + content: ContentManagementService, + private breakpointObserver: BreakpointObserver + ) { + super(store, extensions, content); + } - ngOnInit() { - super.ngOnInit(); + ngOnInit() { + super.ngOnInit(); - this.subscriptions = this.subscriptions.concat([ - this.content.nodesDeleted.subscribe(() => this.reload()), - this.content.nodesMoved.subscribe(() => this.reload()), - this.content.nodesRestored.subscribe(() => this.reload()), - this.content.linksUnshared.subscribe(() => this.reload()), + this.subscriptions = this.subscriptions.concat([ + this.content.nodesDeleted.subscribe(() => this.reload()), + this.content.nodesMoved.subscribe(() => this.reload()), + this.content.nodesRestored.subscribe(() => this.reload()), + this.content.linksUnshared.subscribe(() => this.reload()), - this.breakpointObserver - .observe([ - Breakpoints.HandsetPortrait, - Breakpoints.HandsetLandscape - ]) - .subscribe(result => { - this.isSmallScreen = result.matches; - }) - ]); - } + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .subscribe(result => { + this.isSmallScreen = result.matches; + }) + ]); + } } diff --git a/src/app/components/shared-link-view/shared-link-view.component.scss b/src/app/components/shared-link-view/shared-link-view.component.scss index 39982fce3..00a2c8c84 100644 --- a/src/app/components/shared-link-view/shared-link-view.component.scss +++ b/src/app/components/shared-link-view/shared-link-view.component.scss @@ -1,4 +1,4 @@ .app-shared-link-view { - width: 100%; - height: 100%; + width: 100%; + height: 100%; } diff --git a/src/app/components/shared-link-view/shared-link-view.component.ts b/src/app/components/shared-link-view/shared-link-view.component.ts index 056301dca..ed08b7a77 100644 --- a/src/app/components/shared-link-view/shared-link-view.component.ts +++ b/src/app/components/shared-link-view/shared-link-view.component.ts @@ -2,23 +2,21 @@ import { Component, ViewEncapsulation, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ - selector: 'app-shared-link-view', - templateUrl: 'shared-link-view.component.html', - styleUrls: [ 'shared-link-view.component.scss' ], - encapsulation: ViewEncapsulation.None, - // tslint:disable-next-line:use-host-property-decorator - host: { 'class': 'app-shared-link-view' } + selector: 'app-shared-link-view', + templateUrl: 'shared-link-view.component.html', + styleUrls: ['shared-link-view.component.scss'], + encapsulation: ViewEncapsulation.None, + // tslint:disable-next-line:use-host-property-decorator + host: { class: 'app-shared-link-view' } }) export class SharedLinkViewComponent implements OnInit { + sharedLinkId: string = null; - sharedLinkId: string = null; - - constructor(private route: ActivatedRoute) {} - - ngOnInit() { - this.route.params.subscribe(params => { - this.sharedLinkId = params.id; - }); - } + constructor(private route: ActivatedRoute) {} + ngOnInit() { + this.route.params.subscribe(params => { + this.sharedLinkId = params.id; + }); + } } diff --git a/src/app/components/sidenav/sidenav.component.scss b/src/app/components/sidenav/sidenav.component.scss index 48df44e8f..7a0b9e0d9 100644 --- a/src/app/components/sidenav/sidenav.component.scss +++ b/src/app/components/sidenav/sidenav.component.scss @@ -1,51 +1,51 @@ .sidenav { + display: flex; + flex: 1; + flex-direction: column; + height: 100%; + + &__section:last-child { + border-bottom: 0; + } + + &_action-menu { display: flex; - flex: 1; + padding: 16px 24px; + height: 40px; + justify-content: center; + align-items: center; + } + + &__section { + padding: 8px 14px; + position: relative; + } + + &-menu { + display: inline-flex; flex-direction: column; - height: 100%; + padding: 0; + margin: 0; + list-style-type: none; + } - &__section:last-child { - border-bottom: 0; - } + &-menu__item { + padding: 12px 0; + flex-direction: row; + display: flex; + align-items: center; + text-decoration: none; + text-decoration: none; + height: 24px; + } - &_action-menu { - display: flex; - padding: 16px 24px; - height: 40px; - justify-content: center; - align-items: center; - } + .menu__item--label { + cursor: pointer; + width: 240px; + padding-left: 10px; + } - &__section { - padding: 8px 14px; - position: relative; - } - - &-menu { - display: inline-flex; - flex-direction: column; - padding: 0; - margin: 0; - list-style-type: none; - } - - &-menu__item { - padding: 12px 0; - flex-direction: row; - display: flex; - align-items: center; - text-decoration: none; - text-decoration: none; - height: 24px; - } - - .menu__item--label { - cursor: pointer; - width: 240px; - padding-left: 10px; - } - - .menu__item--label:focus { - outline: none; - } + .menu__item--label:focus { + outline: none; + } } diff --git a/src/app/components/sidenav/sidenav.component.spec.ts b/src/app/components/sidenav/sidenav.component.spec.ts index 24cda9f3c..78a09728a 100644 --- a/src/app/components/sidenav/sidenav.component.spec.ts +++ b/src/app/components/sidenav/sidenav.component.spec.ts @@ -32,29 +32,23 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { ExperimentalDirective } from '../../directives/experimental.directive'; describe('SidenavComponent', () => { - let fixture: ComponentFixture; - let component: SidenavComponent; + let fixture: ComponentFixture; + let component: SidenavComponent; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - AppTestingModule, - EffectsModule.forRoot([NodeEffects]) - ], - declarations: [ - SidenavComponent, - ExperimentalDirective - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(SidenavComponent); - component = fixture.componentInstance; - }); - })); + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule, EffectsModule.forRoot([NodeEffects])], + declarations: [SidenavComponent, ExperimentalDirective], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents() + .then(() => { + fixture = TestBed.createComponent(SidenavComponent); + component = fixture.componentInstance; + }); + })); - it('should be created', () => { - expect(component).toBeTruthy(); - }); + it('should be created', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/components/sidenav/sidenav.component.theme.scss b/src/app/components/sidenav/sidenav.component.theme.scss index 9ec93afae..7c0541ac7 100644 --- a/src/app/components/sidenav/sidenav.component.theme.scss +++ b/src/app/components/sidenav/sidenav.component.theme.scss @@ -1,35 +1,34 @@ @mixin sidenav-component-theme($theme) { - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $foreground: map-get($theme, foreground); - $background: map-get($theme, background); + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $foreground: map-get($theme, foreground); + $background: map-get($theme, background); - $border: 1px solid mat-color($foreground, divider, .07); + $border: 1px solid mat-color($foreground, divider, 0.07); - .sidenav { - @include angular-material-theme($theme); + .sidenav { + @include angular-material-theme($theme); - background-color: mat-color($background, background); + background-color: mat-color($background, background); - .adf-sidebar-action-menu-button { - background-color: mat-color($accent); - } + .adf-sidebar-action-menu-button { + background-color: mat-color($accent); + } - &__section { - border-bottom: $border; - } + &__section { + border-bottom: $border; + } - .menu__item--label:not(.menu__item--active):hover { - color: mat-color($foreground, text); - } + .menu__item--label:not(.menu__item--active):hover { + color: mat-color($foreground, text); + } - .menu__item--active { - color: mat-color($accent); - } - - .menu__item--default { - color: mat-color($primary, .87); - } + .menu__item--active { + color: mat-color($accent); + } + .menu__item--default { + color: mat-color($primary, 0.87); } } +} diff --git a/src/app/components/sidenav/sidenav.component.ts b/src/app/components/sidenav/sidenav.component.ts index 37003e10d..884b59ba1 100644 --- a/src/app/components/sidenav/sidenav.component.ts +++ b/src/app/components/sidenav/sidenav.component.ts @@ -33,39 +33,40 @@ import { takeUntil } from 'rxjs/operators'; import { ContentActionRef, NavBarGroupRef } from '@alfresco/adf-extensions'; @Component({ - selector: 'app-sidenav', - templateUrl: './sidenav.component.html', - styleUrls: ['./sidenav.component.scss'] + selector: 'app-sidenav', + templateUrl: './sidenav.component.html', + styleUrls: ['./sidenav.component.scss'] }) export class SidenavComponent implements OnInit, OnDestroy { - @Input() showLabel: boolean; + @Input() + showLabel: boolean; - groups: Array = []; - createActions: Array = []; - onDestroy$: Subject = new Subject(); + groups: Array = []; + createActions: Array = []; + onDestroy$: Subject = new Subject(); - constructor( - private store: Store, - private extensions: AppExtensionService - ) {} + constructor( + private store: Store, + private extensions: AppExtensionService + ) {} - ngOnInit() { - this.groups = this.extensions.getNavigationGroups(); + ngOnInit() { + this.groups = this.extensions.getNavigationGroups(); - this.store - .select(currentFolder) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(() => { - this.createActions = this.extensions.getCreateActions(); - }); - } + this.store + .select(currentFolder) + .pipe(takeUntil(this.onDestroy$)) + .subscribe(() => { + this.createActions = this.extensions.getCreateActions(); + }); + } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } - trackById(index: number, obj: { id: string }) { - return obj.id; - } + trackById(index: number, obj: { id: string }) { + return obj.id; + } } diff --git a/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts b/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts index 418237829..c30925fae 100644 --- a/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts +++ b/src/app/components/toolbar/document-display-mode/document-display-mode.component.ts @@ -31,26 +31,25 @@ import { documentDisplayMode } from '../../../store/selectors/app.selectors'; import { ToggleDocumentDisplayMode } from '../../../store/actions'; @Component({ - selector: 'app-document-display-mode', - template: ` - - ` + selector: 'app-document-display-mode', + template: ` + + ` }) export class DocumentDisplayModeComponent { + displayMode$: Observable; - displayMode$: Observable; + constructor(private store: Store) { + this.displayMode$ = store.select(documentDisplayMode); + } - constructor(private store: Store) { - this.displayMode$ = store.select(documentDisplayMode); - } - - onClick() { - this.store.dispatch(new ToggleDocumentDisplayMode()); - } + onClick() { + this.store.dispatch(new ToggleDocumentDisplayMode()); + } } diff --git a/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts b/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts index a438a58dd..6d02b70b1 100644 --- a/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts +++ b/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts @@ -32,8 +32,8 @@ import { SelectionState } from '@alfresco/adf-extensions'; import { ContentManagementService } from '../../../services/content-management.service'; @Component({ - selector: 'app-toggle-favorite', - template: ` + selector: 'app-toggle-favorite', + template: ` - ` + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'app-document-display-mode' } }) export class DocumentDisplayModeComponent { displayMode$: Observable; diff --git a/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts b/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts index 6d02b70b1..55a547ced 100644 --- a/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts +++ b/src/app/components/toolbar/toggle-favorite/toggle-favorite.component.ts @@ -23,7 +23,7 @@ * along with Alfresco. If not, see . */ -import { Component } from '@angular/core'; +import { Component, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppStore } from '../../../store/states'; import { appSelection } from '../../../store/selectors/app.selectors'; @@ -43,7 +43,9 @@ import { ContentManagementService } from '../../../services/content-management.s star_border {{ 'APP.ACTIONS.FAVORITE' | translate }} - ` + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toggle-favorite' } }) export class ToggleFavoriteComponent { selection$: Observable; diff --git a/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts b/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts index 990c4127a..4d9f609ec 100644 --- a/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts +++ b/src/app/components/toolbar/toggle-info-drawer/toggle-info-drawer.component.ts @@ -23,7 +23,7 @@ * along with Alfresco. If not, see . */ -import { Component } from '@angular/core'; +import { Component, ViewEncapsulation } from '@angular/core'; import { Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { AppStore } from '../../../store/states'; @@ -40,7 +40,9 @@ import { ToggleInfoDrawerAction } from '../../../store/actions'; (click)="onClick()"> info_outline - ` + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toggle-info-drawer' } }) export class ToggleInfoDrawerComponent { infoDrawerOpened$: Observable; diff --git a/src/app/components/toolbar/toolbar-action/toolbar-action.component.html b/src/app/components/toolbar/toolbar-action/toolbar-action.component.html index b8e747c56..36d720518 100644 --- a/src/app/components/toolbar/toolbar-action/toolbar-action.component.html +++ b/src/app/components/toolbar/toolbar-action/toolbar-action.component.html @@ -1,43 +1,20 @@ - + - + - + - - + - - - - - - - - - - - - - - + - + diff --git a/src/app/components/toolbar/toolbar-action/toolbar-action.component.ts b/src/app/components/toolbar/toolbar-action/toolbar-action.component.ts index eb46f53ae..23f5036db 100644 --- a/src/app/components/toolbar/toolbar-action/toolbar-action.component.ts +++ b/src/app/components/toolbar/toolbar-action/toolbar-action.component.ts @@ -29,10 +29,7 @@ import { ChangeDetectionStrategy, Input } from '@angular/core'; -import { AppStore } from '../../../store/states'; -import { Store } from '@ngrx/store'; import { ContentActionRef } from '@alfresco/adf-extensions'; -import { AppExtensionService } from '../../../extensions/extension.service'; @Component({ selector: 'aca-toolbar-action', @@ -44,15 +41,7 @@ import { AppExtensionService } from '../../../extensions/extension.service'; export class ToolbarActionComponent { @Input() type = 'icon-button'; + @Input() - entry: ContentActionRef; - - constructor( - protected store: Store, - protected extensions: AppExtensionService - ) {} - - trackByActionId(index: number, action: ContentActionRef) { - return action.id; - } + actionRef: ContentActionRef; } diff --git a/src/app/components/toolbar/toolbar-button/toolbar-button.component.html b/src/app/components/toolbar/toolbar-button/toolbar-button.component.html index a8b484f68..896928bf7 100644 --- a/src/app/components/toolbar/toolbar-button/toolbar-button.component.html +++ b/src/app/components/toolbar/toolbar-button/toolbar-button.component.html @@ -10,19 +10,6 @@ - +
diff --git a/src/app/components/toolbar/toolbar-button/toolbar-button.component.ts b/src/app/components/toolbar/toolbar-button/toolbar-button.component.ts index 15b67bd50..6226a7dd8 100644 --- a/src/app/components/toolbar/toolbar-button/toolbar-button.component.ts +++ b/src/app/components/toolbar/toolbar-button/toolbar-button.component.ts @@ -23,12 +23,8 @@ * along with Alfresco. If not, see . */ -import { Component, Input } from '@angular/core'; +import { Component, Input, ViewEncapsulation } from '@angular/core'; import { ContentActionRef } from '@alfresco/adf-extensions'; -import { Store } from '@ngrx/store'; -import { AppStore } from '../../../store/states'; -import { appSelection } from '../../../store/selectors/app.selectors'; -import { take } from 'rxjs/operators'; import { AppExtensionService } from '../../../extensions/extension.service'; export enum ToolbarButtonType { @@ -38,27 +34,29 @@ export enum ToolbarButtonType { @Component({ selector: 'app-toolbar-button', - templateUrl: 'toolbar-button.component.html' + templateUrl: 'toolbar-button.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toolbar-button' } }) export class ToolbarButtonComponent { @Input() type: ToolbarButtonType = ToolbarButtonType.ICON_BUTTON; + @Input() actionRef: ContentActionRef; - constructor( - protected store: Store, - private extensions: AppExtensionService - ) {} + constructor(private extensions: AppExtensionService) {} runAction() { - this.store - .select(appSelection) - .pipe(take(1)) - .subscribe(selection => { - this.extensions.runActionById(this.actionRef.actions.click, { - selection - }); - }); + if (this.hasClickAction(this.actionRef)) { + this.extensions.runActionById(this.actionRef.actions.click); + } + } + + private hasClickAction(actionRef: ContentActionRef): boolean { + if (actionRef && actionRef.actions && actionRef.actions.click) { + return true; + } + return false; } } diff --git a/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html b/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html new file mode 100644 index 000000000..247c9b680 --- /dev/null +++ b/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.html @@ -0,0 +1,14 @@ + diff --git a/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.ts b/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.ts new file mode 100644 index 000000000..b822e59c1 --- /dev/null +++ b/src/app/components/toolbar/toolbar-menu-item/toolbar-menu-item.component.ts @@ -0,0 +1,54 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { ContentActionRef } from '@alfresco/adf-extensions'; +import { AppExtensionService } from '../../../extensions/extension.service'; + +@Component({ + selector: 'app-toolbar-menu-item', + templateUrl: 'toolbar-menu-item.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toolbar-menu-item' } +}) +export class ToolbarMenuItemComponent { + @Input() + actionRef: ContentActionRef; + + constructor(private extensions: AppExtensionService) {} + + runAction() { + if (this.hasClickAction(this.actionRef)) { + this.extensions.runActionById(this.actionRef.actions.click); + } + } + + private hasClickAction(actionRef: ContentActionRef): boolean { + if (actionRef && actionRef.actions && actionRef.actions.click) { + return true; + } + return false; + } +} diff --git a/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html b/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html new file mode 100644 index 000000000..1e47435cc --- /dev/null +++ b/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.ts b/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.ts new file mode 100644 index 000000000..634c583a5 --- /dev/null +++ b/src/app/components/toolbar/toolbar-menu/toolbar-menu.component.ts @@ -0,0 +1,50 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { ContentActionRef } from '@alfresco/adf-extensions'; + +@Component({ + selector: 'app-toolbar-menu', + templateUrl: 'toolbar-menu.component.html', + encapsulation: ViewEncapsulation.None, + host: { class: 'app-toolbar-menu' } +}) +export class ToolbarMenuComponent { + @Input() + actionRef: ContentActionRef; + + get hasChildren(): boolean { + return ( + this.actionRef && + this.actionRef.children && + this.actionRef.children.length > 0 + ); + } + + trackById(index: number, obj: { id: string }) { + return obj.id; + } +} diff --git a/src/app/components/toolbar/toolbar.module.ts b/src/app/components/toolbar/toolbar.module.ts index 32e9b62da..0ff8fb02d 100644 --- a/src/app/components/toolbar/toolbar.module.ts +++ b/src/app/components/toolbar/toolbar.module.ts @@ -32,6 +32,8 @@ import { CoreModule } from '@alfresco/adf-core'; import { ToolbarButtonComponent } from './toolbar-button/toolbar-button.component'; import { ToolbarActionComponent } from './toolbar-action/toolbar-action.component'; import { ExtensionsModule } from '@alfresco/adf-extensions'; +import { ToolbarMenuItemComponent } from './toolbar-menu-item/toolbar-menu-item.component'; +import { ToolbarMenuComponent } from './toolbar-menu/toolbar-menu.component'; export function components() { return [ @@ -39,7 +41,9 @@ export function components() { ToggleFavoriteComponent, ToggleInfoDrawerComponent, ToolbarButtonComponent, - ToolbarActionComponent + ToolbarActionComponent, + ToolbarMenuItemComponent, + ToolbarMenuComponent ]; } diff --git a/src/app/components/trashcan/trashcan.component.html b/src/app/components/trashcan/trashcan.component.html index 947d9868e..84f3c29a0 100644 --- a/src/app/components/trashcan/trashcan.component.html +++ b/src/app/components/trashcan/trashcan.component.html @@ -7,7 +7,7 @@ - + diff --git a/src/app/extensions/extension.service.ts b/src/app/extensions/extension.service.ts index 33908a166..f4004c113 100644 --- a/src/app/extensions/extension.service.ts +++ b/src/app/extensions/extension.service.ts @@ -244,10 +244,13 @@ export class AppExtensionService implements RuleContext { return true; } - runActionById(id: string, context?: any) { + runActionById(id: string) { const action = this.extensions.getActionById(id); if (action) { const { type, payload } = action; + const context = { + selection: this.selection + }; const expression = this.extensions.runExpression(payload, context); this.store.dispatch({ type, payload: expression }); From eb97b18f95af5299f7c7e7b563ad68911f112ca0 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Mon, 17 Sep 2018 04:57:56 +0100 Subject: [PATCH 019/160] feature modules (#636) * permission manager module * permissions module * common module * layout and search modules * trashcan module, lazy load * fix context menu module * lint fix * recent files module * favorites module (lazy) * shared files module (lazy) * shared link module (lazy) * lazy loaading fixes --- docs/extending.md | 1455 ++++++++-------- src/app/app.module.ts | 57 +- src/app/app.routes.ts | 67 +- src/app/components/about/about.module.ts | 5 +- src/app/components/common/common.module.ts | 37 + .../generic-error.component.html | 0 .../generic-error.component.theme.scss | 0 .../generic-error/generic-error.component.ts | 0 .../location-link/location-link.component.ts | 6 +- .../context-menu/context-menu.module.ts | 2 - .../current-user/current-user.module.ts | 37 + .../components/favorites/favorites.module.ts | 64 + src/app/components/layout/layout.module.ts | 52 + src/app/components/login/login.module.ts | 10 +- .../node-permissions.dialog.html | 2 +- .../node-permissions.dialog.ts | 0 .../permission-manager.component.html} | 0 .../permission-manager.component.theme.scss} | 2 +- .../permission-manager.component.ts} | 18 +- .../permissions/permissions.module.ts | 39 + .../recent-files/recent-files.module.ts | 63 + .../components/search/search-input.module.ts | 38 + .../search/search-results.module.ts | 48 + .../components/settings/settings.module.ts | 5 +- .../shared-files/shared-files.module.ts | 64 + .../shared-link-view.module.ts | 59 + .../components/trashcan/trashcan.module.ts | 62 + src/app/extensions/core.extensions.module.ts | 2 - .../services/content-management.service.ts | 2 +- src/app/ui/custom-theme.scss | 6 +- src/assets/app.extensions.json | 1535 ++++++++--------- src/assets/plugins/plugin2.json | 42 - 32 files changed, 2113 insertions(+), 1666 deletions(-) create mode 100644 src/app/components/common/common.module.ts rename src/app/components/{ => common}/generic-error/generic-error.component.html (100%) rename src/app/components/{ => common}/generic-error/generic-error.component.theme.scss (100%) rename src/app/components/{ => common}/generic-error/generic-error.component.ts (100%) rename src/app/components/{ => common}/location-link/location-link.component.ts (96%) create mode 100644 src/app/components/current-user/current-user.module.ts create mode 100644 src/app/components/favorites/favorites.module.ts create mode 100644 src/app/components/layout/layout.module.ts rename src/app/{dialogs/node-permissions => components/permissions/permission-dialog}/node-permissions.dialog.html (79%) rename src/app/{dialogs/node-permissions => components/permissions/permission-dialog}/node-permissions.dialog.ts (100%) rename src/app/components/{permission-manager/permissions-manager.component.html => permissions/permission-manager/permission-manager.component.html} (100%) rename src/app/components/{permission-manager/permissions-manager.component.theme.scss => permissions/permission-manager/permission-manager.component.theme.scss} (95%) rename src/app/components/{permission-manager/permissions-manager.component.ts => permissions/permission-manager/permission-manager.component.ts} (87%) create mode 100644 src/app/components/permissions/permissions.module.ts create mode 100644 src/app/components/recent-files/recent-files.module.ts create mode 100644 src/app/components/search/search-input.module.ts create mode 100644 src/app/components/search/search-results.module.ts create mode 100644 src/app/components/shared-files/shared-files.module.ts create mode 100644 src/app/components/shared-link-view/shared-link-view.module.ts create mode 100644 src/app/components/trashcan/trashcan.module.ts delete mode 100644 src/assets/plugins/plugin2.json diff --git a/docs/extending.md b/docs/extending.md index 9f07fcb2d..860603ad3 100644 --- a/docs/extending.md +++ b/docs/extending.md @@ -18,23 +18,23 @@ already preconfigured to store external files. You can create plugins that change, toggle or extend the following areas: -* Navigation sidebar links and groups -* Context Menu -* Sidebar (aka Info Drawer) -* Toolbar entries - * buttons - * menu buttons - * separators -* Viewer actions - * "Open With" entries - * "More actions" toolbar entries +- Navigation sidebar links and groups +- Context Menu +- Sidebar (aka Info Drawer) +- Toolbar entries + - buttons + - menu buttons + - separators +- Viewer actions + - "Open With" entries + - "More actions" toolbar entries Extensions can also: -* Overwrite or disable extension points of the main application or other plugins -* Change rules, actions or any visual element -* Register new application routes based on empty pages or layouts -* Register new rule evaluators, components, guards, etc. +- Overwrite or disable extension points of the main application or other plugins +- Change rules, actions or any visual element +- Register new application routes based on empty pages or layouts +- Register new rule evaluators, components, guards, etc. ## Format @@ -42,13 +42,13 @@ The format is represented by a JSON file with the structure similar to the follo ```json { - "$name": "app", - "$version": "1.0.0", + "$name": "app", + "$version": "1.0.0", - "routes": [], - "actions": [], - "rules": [], - "features": {} + "routes": [], + "actions": [], + "rules": [], + "features": {} } ``` @@ -62,9 +62,9 @@ Schema allows validating extension files, provides code completion and documenta ```json { - "$schema": "../../extension.schema.json", - "$name": "app", - "$version": "1.0.0", + "$schema": "../../extension.schema.json", + "$name": "app", + "$version": "1.0.0" } ``` @@ -76,13 +76,10 @@ the order of declaration defines also the order of loading. ```json { - "$schema": "../../extension.schema.json", - "$name": "app", - "$version": "1.0.0", - "$references": [ - "plugin1.json", - "plugin2.json" - ] + "$schema": "../../extension.schema.json", + "$name": "app", + "$version": "1.0.0", + "$references": ["plugin1.json", "plugin2.json"] } ``` @@ -119,9 +116,9 @@ Before: Plugin 1 ```json { - "$name": "plugin1", - "plugin1.key": "value", - "plugin1.text": "string" + "$name": "plugin1", + "plugin1.key": "value", + "plugin1.text": "string" } ``` @@ -129,9 +126,9 @@ Before: Plugin 2 ```json { - "$name": "plugin2", - "plugin2.key": "value", - "plugin1.text": "custom string" + "$name": "plugin2", + "plugin2.key": "value", + "plugin1.text": "custom string" } ``` @@ -139,9 +136,9 @@ Final result: ```json { - "plugin1.key": "value", - "plugin1.text": "custom string", - "plugin2.key": "value" + "plugin1.key": "value", + "plugin1.text": "custom string", + "plugin2.key": "value" } ``` @@ -161,13 +158,13 @@ Before: Plugin 1 ```json { - "$name": "plugin1", - "features": { - "title": "some title", - "page1": { - "title": "page 1" - } + "$name": "plugin1", + "features": { + "title": "some title", + "page1": { + "title": "page 1" } + } } ``` @@ -175,15 +172,15 @@ Before: Plugin 2 ```json { - "$name": "plugin2", - "features": { - "page1": { - "title": "custom title" - }, - "page2": { - "title": "page 2" - } + "$name": "plugin2", + "features": { + "page1": { + "title": "custom title" + }, + "page2": { + "title": "page 2" } + } } ``` @@ -191,15 +188,15 @@ Final result: ```json { - "features": { - "title": "some title", - "page1": { - "title": "custom title" - }, - "page2": { - "title": "page 2" - } + "features": { + "title": "some title", + "page1": { + "title": "custom title" + }, + "page2": { + "title": "page 2" } + } } ``` @@ -214,12 +211,12 @@ Before: Plugin 1 ```json { - "$name": "plugin1", - "feature1": { - "disabled": false, - "text": "some-feature", - "icon": "some-icon" - } + "$name": "plugin1", + "feature1": { + "disabled": false, + "text": "some-feature", + "icon": "some-icon" + } } ``` @@ -227,10 +224,10 @@ Before: Plugin 2 ```json { - "$name": "plugin2", - "feature1": { - "disabled": true - } + "$name": "plugin2", + "feature1": { + "disabled": true + } } ``` @@ -238,11 +235,11 @@ Final result: ```json { - "feature1": { - "disabled": true, - "text": "some-feature", - "icon": "some-icon" - } + "feature1": { + "disabled": true, + "text": "some-feature", + "icon": "some-icon" + } } ``` @@ -261,14 +258,14 @@ Before: Plugin 1 ```json { - "$name": "plugin1", - "features": [ - { "text": "common 1" }, - { - "id": "page1", - "text": "page 1" - } - ] + "$name": "plugin1", + "features": [ + { "text": "common 1" }, + { + "id": "page1", + "text": "page 1" + } + ] } ``` @@ -276,14 +273,14 @@ Before: Plugin 2 ```json { - "$name": "plugin2", - "features": [ - { "text": "common 2" }, - { - "id": "page1", - "text": "custom page" - } - ] + "$name": "plugin2", + "features": [ + { "text": "common 2" }, + { + "id": "page1", + "text": "custom page" + } + ] } ``` @@ -291,14 +288,14 @@ Final result: ```json { - "features": [ - { "text": "common 1" }, - { "text": "common 2" }, - { - "id": "page1", - "text": "custom page" - } - ] + "features": [ + { "text": "common 1" }, + { "text": "common 2" }, + { + "id": "page1", + "text": "custom page" + } + ] } ``` @@ -310,21 +307,21 @@ Most of the schema elements can be switched off by using the `disabled` property ```json { - "$schema": "../../extension.schema.json", - "$name": "app", - "$version": "1.0.0", + "$schema": "../../extension.schema.json", + "$name": "app", + "$version": "1.0.0", - "features": { - "create": [ - { - "id": "app.create.folder", - "disabled": true, - "order": 100, - "icon": "create_new_folder", - "title": "Create Folder" - } - ] - } + "features": { + "create": [ + { + "id": "app.create.folder", + "disabled": true, + "order": 100, + "icon": "create_new_folder", + "title": "Create Folder" + } + ] + } } ``` @@ -362,31 +359,31 @@ To create a new route, populate the `routes` section with the corresponding entr ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "routes": [ - { - "id": "plugin1.routes.bin", - "path": "ext/bin", - "layout": "app.layout.main", - "component": "app.components.trashcan" - } - ] + "routes": [ + { + "id": "plugin1.routes.bin", + "path": "ext/bin", + "layout": "app.layout.main", + "component": "your.component.id" + } + ] } ``` ### Route properties -| Name | Description | -| --- | --- | -| **id** | Unique identifier. | -| **path** | Runtime path of the route. | -| **component** | The main [component](#components) to use for the route. | -| layout | The layout [component](#components) to use for the route. | -| auth | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. | -| data | Custom property bag to carry with the route. | +| Name | Description | +| ------------- | -------------------------------------------------------------------------------------- | +| **id** | Unique identifier. | +| **path** | Runtime path of the route. | +| **component** | The main [component](#components) to use for the route. | +| layout | The layout [component](#components) to use for the route. | +| auth | List of [authentication guards](#authentication-guards). Defaults to `[ "app.auth" ]`. | +| data | Custom property bag to carry with the route. | Use the `app.layout.main` value for the `layout` property to get the default application layout, with header, navigation sidebar and main content area. @@ -401,22 +398,22 @@ You can define the full route schema like in the next example: ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "routes": [ - { - "id": "plugin1.routes.bin", - "path": "ext/bin", - "component": "app.components.trashcan", - "layout": "app.layout.main", - "auth": [ "app.auth" ], - "data": { - "title": "Custom Trashcan" - } - } - ] + "routes": [ + { + "id": "plugin1.routes.bin", + "path": "ext/bin", + "component": "your.component.id", + "layout": "app.layout.main", + "auth": ["app.auth"], + "data": { + "title": "Custom Trashcan" + } + } + ] } ``` @@ -429,8 +426,8 @@ Defaults to the `['app.auth']` value. Below is the list of the authentication guards main application registers on startup. -| Key | Type | Description | -| --- | --- | --- | +| Key | Type | Description | +| -------- | ------------ | ------------------------------------------------------------------------- | | app.auth | AuthGuardEcm | ADF guard, validates ACS authentication and redirects to Login if needed. | You can refer those guards from within your custom extensions, @@ -442,16 +439,15 @@ You can register any Angular component to participate in extensibility. The components are used to create custom: -* routes and pages -* toolbar buttons -* menu items +- routes and pages +- toolbar buttons +- menu items -| Key | Type | Description | -| --- | --- | --- | -| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. | -| app.components.trashcan | TrashcanComponent | Trashcan component, used for demo purposes. | -| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. | -| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. | +| Key | Type | Description | +| ---------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------- | +| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. | +| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. | +| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |

See [Registration](#registration) section for more details @@ -463,35 +459,35 @@ besides registering a new one. ## Actions -| Name | Description | -| --- | --- | -| **id** | Unique identifier. | -| **type** | Action type, see [Application Actions](#application-actions) for more details. | -| *payload* | Action payload, a string containing value or expression. | +| Name | Description | +| --------- | ------------------------------------------------------------------------------ | +| **id** | Unique identifier. | +| **type** | Action type, see [Application Actions](#application-actions) for more details. | +| _payload_ | Action payload, a string containing value or expression. | ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "actions": [ - { - "id": "plugin1.actions.settings", - "type": "NAVIGATE_URL", - "payload": "/settings" - }, - { - "id": "plugin1.actions.info", - "type": "SNACKBAR_INFO", - "payload": "I'm a nice little popup raised by extension." - }, - { - "id": "plugin1.actions.node-name", - "type": "SNACKBAR_INFO", - "payload": "$('Action for ' + context.selection.first.entry.name)" - } - ] + "actions": [ + { + "id": "plugin1.actions.settings", + "type": "NAVIGATE_URL", + "payload": "/settings" + }, + { + "id": "plugin1.actions.info", + "type": "SNACKBAR_INFO", + "payload": "I'm a nice little popup raised by extension." + }, + { + "id": "plugin1.actions.node-name", + "type": "SNACKBAR_INFO", + "payload": "$('Action for ' + context.selection.first.entry.name)" + } + ] } ``` @@ -519,20 +515,18 @@ $([1, 2, 1 + 2]) // [1, 2, 3] Application is using NgRx (Reactive libraries for Angular, inspired by Redux). To get more information on NxRx please refer to the following resources: -* [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) +- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510) Most of the application features are already exposed in the form of NgRx Actions and corresponding Effects. You can invoke any action via a single `Store` dispatcher, similar to the following: ```typescript export class MyComponent { + constructor(private store: Store) {} - constructor(private store: Store) {} - - onClick() { - this.store.dispatch(new SearchByTermAction('*')); - } - + onClick() { + this.store.dispatch(new SearchByTermAction('*')); + } } ``` @@ -543,12 +537,11 @@ Another example demonstrates viewing a node from a custom application service AP ```typescript export class MyService { + constructor(private store: Store) {} - constructor(private store: Store) {} - - viewFile(node: MinimalNodeEntity) { - this.store.dispatch(new ViewFileAction(node)); - } + viewFile(node: MinimalNodeEntity) { + this.store.dispatch(new ViewFileAction(node)); + } } ``` @@ -566,23 +559,23 @@ and provide a new `Create Folder (plugin1)` command that invokes the `CREATE_FOL ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "create": [ - { - "id": "plugin1.create.folder", - "type": "default", - "icon": "create_new_folder", - "title": "Create Folder (plugin1)", - "actions": { - "click": "CREATE_FOLDER" - } - } - ] - } + "features": { + "create": [ + { + "id": "plugin1.create.folder", + "type": "default", + "icon": "create_new_folder", + "title": "Create Folder (plugin1)", + "actions": { + "click": "CREATE_FOLDER" + } + } + ] + } } ``` @@ -591,41 +584,41 @@ and perform document list reload if needed. Below is the list of public actions types you can use in the plugin definitions as a reference to the action: -| Name | Payload | Description | -| --- | --- | --- | -| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. | -| SET_CURRENT_URL | string | Notify components about current browser URL. | -| SET_USER_PROFILE | Person | Assign current user profile. | -| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. | -| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. | -| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. | -| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. | -| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. | -| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. | -| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. | -| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). | -| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. | -| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. | -| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. | -| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). | -| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). | -| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). | -| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). | -| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. | -| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. | -| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). | -| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). | -| NAVIGATE_URL | string | Navigate to a given route URL within the application. | -| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters) | -| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. | -| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. | -| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. | -| SNACKBAR_INFO | string | Show information snackbar with the message provided. | -| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. | -| SNACKBAR_ERROR | string | Show error snackbar with the message provided. | -| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. | -| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. | -| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. | +| Name | Payload | Description | +| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------- | +| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. | +| SET_CURRENT_URL | string | Notify components about current browser URL. | +| SET_USER_PROFILE | Person | Assign current user profile. | +| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. | +| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. | +| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. | +| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. | +| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. | +| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. | +| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. | +| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). | +| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. | +| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. | +| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. | +| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). | +| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). | +| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). | +| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). | +| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. | +| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. | +| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). | +| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). | +| NAVIGATE_URL | string | Navigate to a given route URL within the application. | +| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters) | +| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. | +| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. | +| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. | +| SNACKBAR_INFO | string | Show information snackbar with the message provided. | +| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. | +| SNACKBAR_ERROR | string | Show error snackbar with the message provided. | +| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. | +| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. | +| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. | ## Rules @@ -636,16 +629,16 @@ Every rule is backed by a condition evaluator. ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "rules": [ - { - "id": "app.trashcan", - "type": "app.navigation.isTrashcan" - } - ] + "rules": [ + { + "id": "app.trashcan", + "type": "app.navigation.isTrashcan" + } + ] } ``` @@ -653,22 +646,22 @@ Rules can accept other rules as parameters: ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "rules": [ - { - "id": "app.toolbar.favorite.canAdd", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.canAddFavorite" }, - { "type": "rule", "value": "app.navigation.isNotRecentFiles" }, - { "type": "rule", "value": "app.navigation.isNotSharedFiles" }, - { "type": "rule", "value": "app.navigation.isNotSearchResults" } - ] - } - ] + "rules": [ + { + "id": "app.toolbar.favorite.canAdd", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.canAddFavorite" }, + { "type": "rule", "value": "app.navigation.isNotRecentFiles" }, + { "type": "rule", "value": "app.navigation.isNotSharedFiles" }, + { "type": "rule", "value": "app.navigation.isNotSearchResults" } + ] + } + ] } ``` @@ -684,45 +677,45 @@ in case you do not need providing extra parameters, or chaining multiple rules t You can create new rules by chaining other rules and evaluators. -| Key | Description | -| --- | --- | -| core.every | Evaluates to `true` if all chained rules evaluate to `true`. | -| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. | -| core.not | Evaluates to `true` if all chained rules evaluate to `false`. | +| Key | Description | +| ---------- | ----------------------------------------------------------------------------- | +| core.every | Evaluates to `true` if all chained rules evaluate to `true`. | +| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. | +| core.not | Evaluates to `true` if all chained rules evaluate to `false`. | Below is an example of the composite rule definition that combines the following conditions: -* user has selected a single file -* user is not using **Trashcan** page +- user has selected a single file +- user is not using **Trashcan** page ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "rules": [ + "rules": [ + { + "id": "app.toolbar.canViewFile", + "type": "core.every", + "parameters": [ { - "id": "app.toolbar.canViewFile", - "type": "core.every", - "parameters": [ - { - "type": "rule", - "value": "app.selection.file" - }, - { - "type": "rule", - "value": "core.not", - "parameters": [ - { - "type": "rule", - "value": "app.navigation.isTrashcan" - } - ] - } - ] + "type": "rule", + "value": "app.selection.file" + }, + { + "type": "rule", + "value": "core.not", + "parameters": [ + { + "type": "rule", + "value": "app.navigation.isTrashcan" + } + ] } - ] + ] + } + ] } ``` @@ -730,26 +723,26 @@ You can now declare a toolbar button action that is based on the rule above. ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "toolbar": [ - { - "id": "app.toolbar.preview", - "type": "button", - "title": "View File", - "icon": "open_in_browser", - "actions": { - "click": "VIEW_FILE" - }, - "rules": { - "visible": "app.toolbar.canViewFile" - } - }, - ] - } + "features": { + "toolbar": [ + { + "id": "app.toolbar.preview", + "type": "button", + "title": "View File", + "icon": "open_in_browser", + "actions": { + "click": "VIEW_FILE" + }, + "rules": { + "visible": "app.toolbar.canViewFile" + } + } + ] + } } ``` @@ -757,20 +750,20 @@ The button will be visible only when the linked rule evaluates to `true`. ### Application Evaluators -| Key | Description | -| --- | --- | -| app.selection.canDelete | User has permission to delete selected node(s). | -| app.selection.canDownload | User can download selected node(s). | -| app.selection.notEmpty | At least one node is selected. | -| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. | -| app.selection.canAddFavorite | User can add selected node(s) to favorites. | -| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. | -| app.selection.first.canUpdate | User has permission to update selected node(s). | -| app.selection.file | A single File node is selected. | -| app.selection.file.canShare | User is able share selected file. | -| app.selection.library | A single Library node is selected. | -| app.selection.folder | A single Folder node is selected. | -| app.selection.folder.canUpdate | User has permissions to update selected folder. | +| Key | Description | +| ------------------------------- | ------------------------------------------------------------ | +| app.selection.canDelete | User has permission to delete selected node(s). | +| app.selection.canDownload | User can download selected node(s). | +| app.selection.notEmpty | At least one node is selected. | +| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. | +| app.selection.canAddFavorite | User can add selected node(s) to favorites. | +| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. | +| app.selection.first.canUpdate | User has permission to update selected node(s). | +| app.selection.file | A single File node is selected. | +| app.selection.file.canShare | User is able share selected file. | +| app.selection.library | A single Library node is selected. | +| app.selection.folder | A single Folder node is selected. | +| app.selection.folder.canUpdate | User has permissions to update selected folder. | ### Navigation Evaluators @@ -786,22 +779,22 @@ You can also negate any rule by utilizing a `!` prefix: `!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`.

-| Key | Description | -| --- | --- | -| app.navigation.folder.canCreate | User can create content in the currently opened folder. | -| app.navigation.folder.canUpload | User can upload content to the currently opened folder. | -| app.navigation.isTrashcan | User is using **Trashcan** page. | -| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. | -| app.navigation.isLibraries | User is using **Libraries** page. | -| app.navigation.isNotLibraries | Current page is not **Libraries**. | -| app.navigation.isSharedFiles | User is using **Shared Files** page. | -| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. | -| app.navigation.isFavorites | User is using **Favorites** page. | -| app.navigation.isNotFavorites | Current page is not **Favorites** | -| app.navigation.isRecentFiles | User is using **Recent Files** page. | -| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. | -| app.navigation.isSearchResults | User is using **Search Results** page. | -| app.navigation.isNotSearchResults | Current page is not **Search Results**. | +| Key | Description | +| --------------------------------- | ------------------------------------------------------- | +| app.navigation.folder.canCreate | User can create content in the currently opened folder. | +| app.navigation.folder.canUpload | User can upload content to the currently opened folder. | +| app.navigation.isTrashcan | User is using **Trashcan** page. | +| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. | +| app.navigation.isLibraries | User is using **Libraries** page. | +| app.navigation.isNotLibraries | Current page is not **Libraries**. | +| app.navigation.isSharedFiles | User is using **Shared Files** page. | +| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. | +| app.navigation.isFavorites | User is using **Favorites** page. | +| app.navigation.isNotFavorites | Current page is not **Favorites** | +| app.navigation.isRecentFiles | User is using **Recent Files** page. | +| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. | +| app.navigation.isSearchResults | User is using **Search Results** page. | +| app.navigation.isNotSearchResults | Current page is not **Search Results**. |

See [Registration](#registration) section for more details @@ -812,27 +805,27 @@ on how to register your own entries to be re-used at runtime. The rule in the example below evaluates to `true` if all the conditions are met: -* user has selected node(s) -* user is not using **Trashcan** page -* user is not using **Libraries** page +- user has selected node(s) +- user is not using **Trashcan** page +- user is not using **Libraries** page ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "rules": [ - { - "id": "app.toolbar.canCopyNode", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.notEmpty" }, - { "type": "rule", "value": "app.navigation.isNotTrashcan" }, - { "type": "rule", "value": "app.navigation.isNotLibraries" } - ] - } - ] + "rules": [ + { + "id": "app.toolbar.canCopyNode", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.notEmpty" }, + { "type": "rule", "value": "app.navigation.isNotTrashcan" }, + { "type": "rule", "value": "app.navigation.isNotLibraries" } + ] + } + ] } ``` @@ -842,33 +835,33 @@ This section contains application-specific features that may vary depending on t The ACA supports the following set of extension points: -* Create menu -* Navigation Bar -* Toolbar -* Context Menu -* Viewer -* Sidebar (aka Info Drawer) +- Create menu +- Navigation Bar +- Toolbar +- Context Menu +- Viewer +- Sidebar (aka Info Drawer) All the customisations are stored in the `features` section of the configuration file: ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "create": [], - "navbar": [], - "toolbar": [], - "contextMenu": [], - "viewer": { - "toolbar:": [], - "openWith": [], - "content": [] - }, - "sidebar": [] - } + "features": { + "create": [], + "navbar": [], + "toolbar": [], + "contextMenu": [], + "viewer": { + "toolbar:": [], + "openWith": [], + "content": [] + }, + "sidebar": [] + } } ``` @@ -882,25 +875,25 @@ are based on `ContentActionRef` interface implementation: ```ts interface ContentActionRef { - id: string; - type: ContentActionType; + id: string; + type: ContentActionType; - title?: string; - description?: string; - order?: number; - icon?: string; - disabled?: boolean; - children?: Array; - component?: string; - actions?: { - click?: string; - [key: string]: string; - }; - rules?: { - enabled?: string; - visible?: string; - [key: string]: string; - }; + title?: string; + description?: string; + order?: number; + icon?: string; + disabled?: boolean; + children?: Array; + component?: string; + actions?: { + click?: string; + [key: string]: string; + }; + rules?: { + enabled?: string; + visible?: string; + [key: string]: string; + }; } ``` @@ -914,36 +907,36 @@ You can populate the menu with an extra entries like in the example below: ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "create": [ - { - "id": "plugin1.create.folder", - "icon": "create_new_folder", - "title": "Create Folder (plugin1)", - "actions": { - "click": "CREATE_FOLDER" - }, - "rules": { - "enabled": "app.navigation.folder.canCreate" - } - }, - { - "id": "plugin1.create.uploadFile", - "icon": "file_upload", - "title": "Upload Files (plugin1)", - "actions": { - "click": "UPLOAD_FILES" - }, - "rules": { - "enabled": "app.navigation.folder.canUpload" - } - } - ] - } + "features": { + "create": [ + { + "id": "plugin1.create.folder", + "icon": "create_new_folder", + "title": "Create Folder (plugin1)", + "actions": { + "click": "CREATE_FOLDER" + }, + "rules": { + "enabled": "app.navigation.folder.canCreate" + } + }, + { + "id": "plugin1.create.uploadFile", + "icon": "file_upload", + "title": "Upload Files (plugin1)", + "actions": { + "click": "UPLOAD_FILES" + }, + "rules": { + "enabled": "app.navigation.folder.canUpload" + } + } + ] + } } ``` @@ -959,71 +952,71 @@ Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups ```ts export interface NavBarGroupRef { - id: string; - items: Array; + id: string; + items: Array; - order?: number; - disabled?: boolean; + order?: number; + disabled?: boolean; } export interface NavBarLinkRef { - id: string; - icon: string; - title: string; - route: string; + id: string; + icon: string; + title: string; + route: string; - url?: string; // evaluated at runtime based on route ref - description?: string; - order?: number; - disabled?: boolean; + url?: string; // evaluated at runtime based on route ref + description?: string; + order?: number; + disabled?: boolean; } ``` You extensions can perform the following actions at runtime: -* Register new groups with links -* Insert new links into existing groups -* Update properties of the existing links -* Disable existing links or entire groups +- Register new groups with links +- Insert new links into existing groups +- Update properties of the existing links +- Disable existing links or entire groups ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "navbar": [ - { - "id": "app.navbar.primary", - "items": [ - { - "id": "app.navbar.personalFiles", - "icon": "folder", - "title": "Personal Files", - "route": "personal-files" - }, - { - "id": "app.navbar.libraries", - "icon": "group_work", - "title": "Libraries", - "route": "libraries" - } - ] - }, - { - "id": "app.navbar.secondary", - "items": [ - { - "id": "app.navbar.shared", - "icon": "people", - "title": "Shared", - "route": "shared" - } - ] - } + "features": { + "navbar": [ + { + "id": "app.navbar.primary", + "items": [ + { + "id": "app.navbar.personalFiles", + "icon": "folder", + "title": "Personal Files", + "route": "personal-files" + }, + { + "id": "app.navbar.libraries", + "icon": "group_work", + "title": "Libraries", + "route": "libraries" + } ] - } + }, + { + "id": "app.navbar.secondary", + "items": [ + { + "id": "app.navbar.shared", + "icon": "people", + "title": "Shared", + "route": "shared" + } + ] + } + ] + } } ``` @@ -1031,39 +1024,39 @@ You extensions can perform the following actions at runtime: You can provide the following customisations for the Sidebar (aka Info Drawer) component: -* Add extra tabs with custom components -* Disable tabs from the main application or extensions -* Replace content or properties of existing tabs +- Add extra tabs with custom components +- Disable tabs from the main application or extensions +- Replace content or properties of existing tabs ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "sidebar": [ - { - "id": "app.sidebar.properties", - "order": 100, - "title": "Properties", - "component": "app.components.tabs.metadata" - }, - { - "id": "app.sidebar.comments", - "order": 200, - "title": "Comments", - "component": "app.components.tabs.comments" - } - ] - } + "features": { + "sidebar": [ + { + "id": "app.sidebar.properties", + "order": 100, + "title": "Properties", + "component": "app.components.tabs.metadata" + }, + { + "id": "app.sidebar.comments", + "order": 200, + "title": "Comments", + "component": "app.components.tabs.comments" + } + ] + } } ``` The example above renders two tabs: -* `Properties` tab that references `app.components.tabs.metadata` component -* `Comments` tab that references `app.components.tabs.comments` component +- `Properties` tab that references `app.components.tabs.metadata` component +- `Comments` tab that references `app.components.tabs.comments` component All corresponding components must be registered for runtime use. @@ -1074,21 +1067,21 @@ on how to register your own entries to be re-used at runtime. #### Tab properties -| Name | Description | -| --- | --- | -| **id** | Unique identifier. | -| **component** | The main [component](#components) to use for the route. | -| **title** | Tab title or resource key. | -| icon | Tab icon | -| disabled | Toggles disabled state. Can be assigned from other plugins. | -| order | The order of the element. | +| Name | Description | +| ------------- | ----------------------------------------------------------- | +| **id** | Unique identifier. | +| **component** | The main [component](#components) to use for the route. | +| **title** | Tab title or resource key. | +| icon | Tab icon | +| disabled | Toggles disabled state. Can be assigned from other plugins. | +| order | The order of the element. | #### Tab components Every component you assign for the tab content receives the following additional properties at runtime: -| Name | Type | Description | -| --- | --- | --- | +| Name | Type | Description | +| ---- | ---------------------- | --------------------------- | | node | MinimalNodeEntryEntity | Node entry to be displayed. | ### Toolbar @@ -1097,48 +1090,48 @@ The toolbar extension point is represented by an array of Content Action referen ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "toolbar": [ - { - "id": "app.toolbar.preview", - "title": "View", - "icon": "open_in_browser", - "actions": { - "click": "VIEW_FILE" - }, - "rules": { - "visible": "app.toolbar.canViewFile" - } - }, - { - "id": "app.toolbar.download", - "title": "Download", - "icon": "get_app", - "actions": { - "click": "DOWNLOAD_NODES" - }, - "rules": { - "visible": "app.toolbar.canDownload" - } - } - ] - } + "features": { + "toolbar": [ + { + "id": "app.toolbar.preview", + "title": "View", + "icon": "open_in_browser", + "actions": { + "click": "VIEW_FILE" + }, + "rules": { + "visible": "app.toolbar.canViewFile" + } + }, + { + "id": "app.toolbar.download", + "title": "Download", + "icon": "get_app", + "actions": { + "click": "DOWNLOAD_NODES" + }, + "rules": { + "visible": "app.toolbar.canDownload" + } + } + ] + } } ``` The content actions are applied to the toolbars for the following Views: -* Personal Files -* Libraries -* Shared -* Recent Files -* Favorites -* Trash -* Search Results +- Personal Files +- Libraries +- Shared +- Recent Files +- Favorites +- Trash +- Search Results ### Context Menu @@ -1147,26 +1140,26 @@ You may want to define a list of content actions backed by Rules and wired with ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "contextMenu": [ - { - "id": "app.context.menu.download", - "order": 100, - "title": "Download", - "icon": "get_app", - "actions": { - "click": "DOWNLOAD_NODES" - }, - "rules": { - "visible": "app.toolbar.canDownload" - } - }, - ] - } + "features": { + "contextMenu": [ + { + "id": "app.context.menu.download", + "order": 100, + "title": "Download", + "icon": "get_app", + "actions": { + "click": "DOWNLOAD_NODES" + }, + "rules": { + "visible": "app.toolbar.canDownload" + } + } + ] + } } ``` @@ -1177,16 +1170,16 @@ declared in the `rules` section: ```json { - "rules": [ - { - "id": "app.toolbar.canDownload", - "type": "core.every", - "parameters": [ - { "type": "rule", "value": "app.selection.canDownload" }, - { "type": "rule", "value": "app.navigation.isNotTrashcan" } - ] - } - ] + "rules": [ + { + "id": "app.toolbar.canDownload", + "type": "core.every", + "parameters": [ + { "type": "rule", "value": "app.selection.canDownload" }, + { "type": "rule", "value": "app.navigation.isNotTrashcan" } + ] + } + ] } ``` @@ -1194,23 +1187,23 @@ declared in the `rules` section: Viewer component in ACA supports the following extension points: -* Content Viewers -* `More` toolbar actions -* `Open With` actions +- Content Viewers +- `More` toolbar actions +- `Open With` actions ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "viewer": { - "content": [], - "toolbar:": [], - "openWith": [] - } + "features": { + "viewer": { + "content": [], + "toolbar:": [], + "openWith": [] } + } } ``` @@ -1220,26 +1213,26 @@ You can provide custom components that render particular type of the content bas ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "viewer": { - "content": [ - { - "id": "app.viewer.pdf", - "fileExtension": "pdf", - "component": "app.components.tabs.metadata" - }, - { - "id": "app.viewer.docx", - "fileExtension": "docx", - "component": "app.components.tabs.comments" - } - ] + "features": { + "viewer": { + "content": [ + { + "id": "app.viewer.pdf", + "fileExtension": "pdf", + "component": "app.components.tabs.metadata" + }, + { + "id": "app.viewer.docx", + "fileExtension": "docx", + "component": "app.components.tabs.comments" } + ] } + } } ``` @@ -1248,11 +1241,11 @@ and `DOCX` view with the `comments` tab. Every custom component receives the following properties at runtime: -| Name | Type | Description | -| --- | --- | --- | -| node | MinimalNodeEntryEntity | Node entry to be displayed. | -| url | string | File content URL. | -| extension | string | File name extension. | +| Name | Type | Description | +| --------- | ---------------------- | --------------------------- | +| node | MinimalNodeEntryEntity | Node entry to be displayed. | +| url | string | File content URL. | +| extension | string | File name extension. | #### Toolbar actions @@ -1261,28 +1254,28 @@ The ACA provides an extension point for this menu that you can utilise to popula ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "features": { - "viewer": { - "toolbar:": [ - { - "id": "app.viewer.share", - "order": 300, - "title": "Share", - "icon": "share", - "actions": { - "click": "SHARE_NODE" - }, - "rules": { - "visible": "app.selection.file.canShare" - } - } - ] + "features": { + "viewer": { + "toolbar:": [ + { + "id": "app.viewer.share", + "order": 300, + "title": "Share", + "icon": "share", + "actions": { + "click": "SHARE_NODE" + }, + "rules": { + "visible": "app.selection.file.canShare" + } } + ] } + } } ``` @@ -1295,33 +1288,33 @@ and invoke it from the custom `Open With` menu entry called `Snackbar`. ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "actions": [ - { - "id": "plugin1.actions.info", - "type": "SNACKBAR_INFO", - "payload": "I'm a nice little popup raised by extension." - }, - ], - - "features": { - "viewer": { - "openWith": [ - { - "id": "plugin1.viewer.openWith.action1", - "type": "button", - "icon": "build", - "title": "Snackbar", - "actions": { - "click": "plugin1.actions.info" - } - } - ] - } + "actions": [ + { + "id": "plugin1.actions.info", + "type": "SNACKBAR_INFO", + "payload": "I'm a nice little popup raised by extension." } + ], + + "features": { + "viewer": { + "openWith": [ + { + "id": "plugin1.viewer.openWith.action1", + "type": "button", + "icon": "build", + "title": "Snackbar", + "actions": { + "click": "plugin1.actions.info" + } + } + ] + } + } } ``` @@ -1393,10 +1386,10 @@ Create a function that is going to check if user has selected one or multiple no ```typescript export function hasSelection( - context: RuleContext, - ...args: RuleParameter[] + context: RuleContext, + ...args: RuleParameter[] ): boolean { - return !context.selection.isEmpty; + return !context.selection.isEmpty; } ``` @@ -1405,11 +1398,11 @@ that provides each evaluator access to runtime entities. ```typescript export interface RuleContext { - selection: SelectionState; - navigation: NavigationState; - permissions: NodePermissions; + selection: SelectionState; + navigation: NavigationState; + permissions: NodePermissions; - getEvaluator(key: string): RuleEvaluator; + getEvaluator(key: string): RuleEvaluator; } ``` @@ -1417,15 +1410,15 @@ The `SelectionState` interface exposes information about global selection state: ```typescript export interface SelectionState { - count: number; - nodes: MinimalNodeEntity[]; - libraries: SiteEntry[]; - isEmpty: boolean; - first?: MinimalNodeEntity; - last?: MinimalNodeEntity; - folder?: MinimalNodeEntity; - file?: MinimalNodeEntity; - library?: SiteEntry; + count: number; + nodes: MinimalNodeEntity[]; + libraries: SiteEntry[]; + isEmpty: boolean; + first?: MinimalNodeEntity; + last?: MinimalNodeEntity; + folder?: MinimalNodeEntity; + file?: MinimalNodeEntity; + library?: SiteEntry; } ``` @@ -1433,7 +1426,7 @@ Next, register the function you have created earlier with the `ExtensionService` ```typescript extensions.setEvaluators({ - 'plugin1.rules.hasSelection': hasSelection + 'plugin1.rules.hasSelection': hasSelection }); ``` @@ -1451,10 +1444,10 @@ on how to register your own entries to be re-used at runtime. In this tutorial, we are going to implement the following features: -* update the **Trashcan** component to receive and log route parameters -* create a new route that points to the **Trashcan** component and uses main layout -* create an action reference that allows redirecting to the new route -* create a button in the **New** menu that invokes an action +- update the **Trashcan** component to receive and log route parameters +- create a new route that points to the **Trashcan** component and uses main layout +- create an action reference that allows redirecting to the new route +- create a button in the **New** menu that invokes an action Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body: @@ -1487,19 +1480,19 @@ Next, add a new route definition as in the example below: ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", - "routes": [ - { - "id": "custom.routes.trashcan", - "path": "ext/trashcan/:nodeId", - "component": "app.components.trashcan", - "layout": "app.layout.main", - "auth": [ "app.auth" ] - } - ] + "routes": [ + { + "id": "custom.routes.trashcan", + "path": "ext/trashcan/:nodeId", + "component": "your.component.id", + "layout": "app.layout.main", + "auth": ["app.auth"] + } + ] } ``` @@ -1587,9 +1580,9 @@ ng generate library my-extension You will get a new project in the `projects/my-extensions` folder. By default, the project contains at least the following content: -* Example component `my-extension.component.ts` -* Example service `my-extension.service.ts` -* Angular Module example `my-extension.module.ts` +- Example component `my-extension.component.ts` +- Example service `my-extension.service.ts` +- Angular Module example `my-extension.module.ts` Next, build the project with the following command: @@ -1611,7 +1604,7 @@ Update `my-extension.module.ts` and put all the content you plan to use at runti exports: [MyExtensionComponent], entryComponents: [MyExtensionComponent] }) -export class MyExtensionModule { } +export class MyExtensionModule {} ``` Let's now register `MyExtensionComponent` as an extension component. @@ -1639,34 +1632,34 @@ Create a new `assets/my-extension.json` file in the library project root folder ```json { - "$schema": "../../../extension.schema.json", - "$version": "1.0.0", - "$name": "plugin1", - "$description": "demo plugin", + "$schema": "../../../extension.schema.json", + "$version": "1.0.0", + "$name": "plugin1", + "$description": "demo plugin", - "routes": [ - { - "id": "my.extension.route", - "path": "ext/my/route", - "component": "my-extension.main.component" - } - ], - - "features": { - "navbar": [ - { - "id": "my.extension.nav", - "items": [ - { - "id": "my.extension.main", - "icon": "extension", - "title": "My Extension", - "route": "my.extension.route" - } - ] - } - ] + "routes": [ + { + "id": "my.extension.route", + "path": "ext/my/route", + "component": "my-extension.main.component" } + ], + + "features": { + "navbar": [ + { + "id": "my.extension.nav", + "items": [ + { + "id": "my.extension.main", + "icon": "extension", + "title": "My Extension", + "route": "my.extension.route" + } + ] + } + ] + } } ``` @@ -1733,9 +1726,9 @@ Edit `angular.json` configuration file and add the following rule if you develop ```json { - "glob": "**/*.json", - "input": "dist/my-extension/assets", - "output": "/assets/plugins" + "glob": "**/*.json", + "input": "dist/my-extension/assets", + "output": "/assets/plugins" } ``` @@ -1743,9 +1736,9 @@ Use the following rule in case you are installing extension from NPM: ```json { - "glob": "**/*.json", - "input": "node_modules/my-extension/assets", - "output": "/assets/plugins" + "glob": "**/*.json", + "input": "node_modules/my-extension/assets", + "output": "/assets/plugins" } ``` diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ba7c26561..f1598ca89 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -39,26 +39,13 @@ import { ContentModule } from '@alfresco/adf-content-services'; import { AppComponent } from './app.component'; import { APP_ROUTES } from './app.routes'; -import { GenericErrorComponent } from './components/generic-error/generic-error.component'; import { FilesComponent } from './components/files/files.component'; -import { FavoritesComponent } from './components/favorites/favorites.component'; import { LibrariesComponent } from './components/libraries/libraries.component'; -import { RecentFilesComponent } from './components/recent-files/recent-files.component'; -import { SharedFilesComponent } from './components/shared-files/shared-files.component'; -import { TrashcanComponent } from './components/trashcan/trashcan.component'; -import { LayoutComponent } from './components/layout/layout.component'; -import { SidenavViewsManagerDirective } from './components/layout/sidenav-views-manager.directive'; -import { CurrentUserComponent } from './components/current-user/current-user.component'; -import { SearchInputComponent } from './components/search/search-input/search-input.component'; -import { SearchInputControlComponent } from './components/search/search-input-control/search-input-control.component'; -import { LocationLinkComponent } from './components/location-link/location-link.component'; -import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component'; import { NodeVersionsDialogComponent } from './dialogs/node-versions/node-versions.dialog'; import { LibraryDialogComponent } from './dialogs/library/library.dialog'; import { ContentManagementService } from './services/content-management.service'; import { NodeActionsService } from './services/node-actions.service'; import { NodePermissionService } from './services/node-permission.service'; -import { SearchResultsComponent } from './components/search/search-results/search-results.component'; import { ProfileResolver } from './services/profile.resolver'; import { ExperimentalGuard } from './services/experimental-guard.service'; @@ -67,9 +54,6 @@ import { MaterialModule } from './material.module'; import { ContentApiService } from './services/content-api.service'; import { AppExtensionsModule } from './extensions.module'; import { CoreExtensionsModule } from './extensions/core.extensions.module'; -import { SearchResultsRowComponent } from './components/search/search-results-row/search-results-row.component'; -import { NodePermissionsDialogComponent } from './dialogs/node-permissions/node-permissions.dialog'; -import { PermissionsManagerComponent } from './components/permission-manager/permissions-manager.component'; import { AppRouteReuseStrategy } from './app.routes.strategy'; import { AppInfoDrawerModule } from './components/info-drawer/info.drawer.module'; import { DirectivesModule } from './directives/directives.module'; @@ -78,6 +62,13 @@ import { ExtensionsModule } from '@alfresco/adf-extensions'; import { AppToolbarModule } from './components/toolbar/toolbar.module'; import { AppCreateMenuModule } from './components/create-menu/create-menu.module'; import { AppSidenavModule } from './components/sidenav/sidenav.module'; +import { AppPermissionsModule } from './components/permissions/permissions.module'; +import { AppCommonModule } from './components/common/common.module'; +import { AppLayoutModule } from './components/layout/layout.module'; +import { AppCurrentUserModule } from './components/current-user/current-user.module'; +import { AppSearchInputModule } from './components/search/search-input.module'; +import { AppSearchResultsModule } from './components/search/search-results.module'; +import { AppLoginModule } from './components/login/login.module'; @NgModule({ imports: [ @@ -96,36 +87,26 @@ import { AppSidenavModule } from './components/sidenav/sidenav.module'; CoreExtensionsModule.forRoot(), ExtensionsModule.forRoot(), AppExtensionsModule, - + AppLoginModule, + AppCommonModule, + AppLayoutModule, + AppCurrentUserModule, DirectivesModule, ContextMenuModule.forRoot(), AppInfoDrawerModule, AppToolbarModule, AppSidenavModule, - AppCreateMenuModule + AppCreateMenuModule, + AppPermissionsModule, + AppSearchInputModule, + AppSearchResultsModule ], declarations: [ AppComponent, - GenericErrorComponent, - LayoutComponent, - SidenavViewsManagerDirective, - CurrentUserComponent, - SearchInputComponent, - SearchInputControlComponent, FilesComponent, - FavoritesComponent, LibrariesComponent, - RecentFilesComponent, - SharedFilesComponent, - TrashcanComponent, - LocationLinkComponent, - SearchResultsRowComponent, NodeVersionsDialogComponent, - LibraryDialogComponent, - NodePermissionsDialogComponent, - PermissionsManagerComponent, - SearchResultsComponent, - SharedLinkViewComponent + LibraryDialogComponent ], providers: [ { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, @@ -145,11 +126,7 @@ import { AppSidenavModule } from './components/sidenav/sidenav.module'; ExperimentalGuard, ContentApiService ], - entryComponents: [ - LibraryDialogComponent, - NodeVersionsDialogComponent, - NodePermissionsDialogComponent - ], + entryComponents: [LibraryDialogComponent, NodeVersionsDialogComponent], bootstrap: [AppComponent] }) export class AppModule {} diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index e952ab102..f4e15dfdb 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -25,26 +25,18 @@ import { Routes } from '@angular/router'; import { AuthGuardEcm } from '@alfresco/adf-core'; -import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component'; - import { LayoutComponent } from './components/layout/layout.component'; - import { FilesComponent } from './components/files/files.component'; -import { FavoritesComponent } from './components/favorites/favorites.component'; import { LibrariesComponent } from './components/libraries/libraries.component'; -import { RecentFilesComponent } from './components/recent-files/recent-files.component'; -import { SharedFilesComponent } from './components/shared-files/shared-files.component'; -import { TrashcanComponent } from './components/trashcan/trashcan.component'; - -import { GenericErrorComponent } from './components/generic-error/generic-error.component'; +import { GenericErrorComponent } from './components/common/generic-error/generic-error.component'; import { SearchResultsComponent } from './components/search/search-results/search-results.component'; - import { ProfileResolver } from './services/profile.resolver'; +import { LoginComponent } from './components/login/login.component'; export const APP_ROUTES: Routes = [ { path: 'login', - loadChildren: 'src/app/components/login/login.module#AppLoginModule', + component: LoginComponent, data: { title: 'APP.SIGN_IN' } @@ -52,17 +44,12 @@ export const APP_ROUTES: Routes = [ { path: 'settings', loadChildren: - 'src/app/components/settings/settings.module#AppSettingsModule', - data: { - title: 'Settings' - } + 'src/app/components/settings/settings.module#AppSettingsModule' }, { path: 'preview/s/:id', - component: SharedLinkViewComponent, - data: { - title: 'APP.PREVIEW.TITLE' - } + loadChildren: + 'src/app/components/shared-link-view/shared-link-view.module#AppSharedLinkViewModule' }, { path: '', @@ -76,16 +63,11 @@ export const APP_ROUTES: Routes = [ }, { path: 'favorites', - data: { - sortingPreferenceKey: 'favorites' - }, children: [ { path: '', - component: FavoritesComponent, - data: { - title: 'APP.BROWSE.FAVORITES.TITLE' - } + loadChildren: + 'src/app/components/favorites/favorites.module#AppFavoritesModule' }, { path: 'preview/:nodeId', @@ -101,15 +83,13 @@ export const APP_ROUTES: Routes = [ }, { path: 'libraries', - data: { - sortingPreferenceKey: 'libraries' - }, children: [ { path: '', component: LibrariesComponent, data: { - title: 'APP.BROWSE.LIBRARIES.TITLE' + title: 'APP.BROWSE.LIBRARIES.TITLE', + sortingPreferenceKey: 'libraries' } }, { @@ -183,10 +163,8 @@ export const APP_ROUTES: Routes = [ children: [ { path: '', - component: RecentFilesComponent, - data: { - title: 'APP.BROWSE.RECENT.TITLE' - } + loadChildren: + 'src/app/components/recent-files/recent-files.module#AppRecentFilesModule' }, { path: 'preview/:nodeId', @@ -202,16 +180,11 @@ export const APP_ROUTES: Routes = [ }, { path: 'shared', - data: { - sortingPreferenceKey: 'shared-files' - }, children: [ { path: '', - component: SharedFilesComponent, - data: { - title: 'APP.BROWSE.SHARED.TITLE' - } + loadChildren: + 'src/app/components/shared-files/shared-files.module#AppSharedFilesModule' }, { path: 'preview/:nodeId', @@ -227,18 +200,12 @@ export const APP_ROUTES: Routes = [ }, { path: 'trashcan', - component: TrashcanComponent, - data: { - title: 'APP.BROWSE.TRASHCAN.TITLE', - sortingPreferenceKey: 'trashcan' - } + loadChildren: + 'src/app/components/trashcan/trashcan.module#AppTrashcanModule' }, { path: 'about', - loadChildren: 'src/app/components/about/about.module#AboutModule', - data: { - title: 'APP.BROWSE.ABOUT.TITLE' - } + loadChildren: 'src/app/components/about/about.module#AboutModule' }, { path: 'search', diff --git a/src/app/components/about/about.module.ts b/src/app/components/about/about.module.ts index 38ada662f..8a25b3334 100644 --- a/src/app/components/about/about.module.ts +++ b/src/app/components/about/about.module.ts @@ -32,7 +32,10 @@ import { CoreModule } from '@alfresco/adf-core'; const routes: Routes = [ { path: '', - component: AboutComponent + component: AboutComponent, + data: { + title: 'APP.BROWSE.ABOUT.TITLE' + } } ]; diff --git a/src/app/components/common/common.module.ts b/src/app/components/common/common.module.ts new file mode 100644 index 000000000..3a82f6d78 --- /dev/null +++ b/src/app/components/common/common.module.ts @@ -0,0 +1,37 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { GenericErrorComponent } from './generic-error/generic-error.component'; +import { CoreModule } from '@alfresco/adf-core'; +import { LocationLinkComponent } from './location-link/location-link.component'; + +@NgModule({ + imports: [CommonModule, CoreModule.forChild()], + declarations: [GenericErrorComponent, LocationLinkComponent], + exports: [GenericErrorComponent, LocationLinkComponent] +}) +export class AppCommonModule {} diff --git a/src/app/components/generic-error/generic-error.component.html b/src/app/components/common/generic-error/generic-error.component.html similarity index 100% rename from src/app/components/generic-error/generic-error.component.html rename to src/app/components/common/generic-error/generic-error.component.html diff --git a/src/app/components/generic-error/generic-error.component.theme.scss b/src/app/components/common/generic-error/generic-error.component.theme.scss similarity index 100% rename from src/app/components/generic-error/generic-error.component.theme.scss rename to src/app/components/common/generic-error/generic-error.component.theme.scss diff --git a/src/app/components/generic-error/generic-error.component.ts b/src/app/components/common/generic-error/generic-error.component.ts similarity index 100% rename from src/app/components/generic-error/generic-error.component.ts rename to src/app/components/common/generic-error/generic-error.component.ts diff --git a/src/app/components/location-link/location-link.component.ts b/src/app/components/common/location-link/location-link.component.ts similarity index 96% rename from src/app/components/location-link/location-link.component.ts rename to src/app/components/common/location-link/location-link.component.ts index 54c6a36c4..56085f7eb 100644 --- a/src/app/components/location-link/location-link.component.ts +++ b/src/app/components/common/location-link/location-link.component.ts @@ -35,9 +35,9 @@ import { PathInfo, MinimalNodeEntity } from 'alfresco-js-api'; import { Observable, BehaviorSubject, of } from 'rxjs'; import { Store } from '@ngrx/store'; -import { AppStore } from '../../store/states/app.state'; -import { NavigateToParentFolder } from '../../store/actions'; -import { ContentApiService } from '../../services/content-api.service'; +import { AppStore } from '../../../store/states/app.state'; +import { NavigateToParentFolder } from '../../../store/actions'; +import { ContentApiService } from '../../../services/content-api.service'; @Component({ selector: 'aca-location-link', diff --git a/src/app/components/context-menu/context-menu.module.ts b/src/app/components/context-menu/context-menu.module.ts index 91720b90e..94321bed6 100644 --- a/src/app/components/context-menu/context-menu.module.ts +++ b/src/app/components/context-menu/context-menu.module.ts @@ -30,7 +30,6 @@ import { MatIconModule, MatButtonModule } from '@angular/material'; -import { BrowserModule } from '@angular/platform-browser'; import { CoreModule } from '@alfresco/adf-core'; import { CoreExtensionsModule } from '../../extensions/core.extensions.module'; @@ -47,7 +46,6 @@ import { OutsideEventDirective } from './context-menu-outside-event.directive'; MatListModule, MatIconModule, MatButtonModule, - BrowserModule, CoreExtensionsModule.forChild(), CoreModule.forChild(), ExtensionsModule.forChild() diff --git a/src/app/components/current-user/current-user.module.ts b/src/app/components/current-user/current-user.module.ts new file mode 100644 index 000000000..29c4de29a --- /dev/null +++ b/src/app/components/current-user/current-user.module.ts @@ -0,0 +1,37 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CoreModule } from '@alfresco/adf-core'; +import { CurrentUserComponent } from './current-user.component'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [CommonModule, CoreModule.forChild(), RouterModule], + declarations: [CurrentUserComponent], + exports: [CurrentUserComponent] +}) +export class AppCurrentUserModule {} diff --git a/src/app/components/favorites/favorites.module.ts b/src/app/components/favorites/favorites.module.ts new file mode 100644 index 000000000..dc066d2de --- /dev/null +++ b/src/app/components/favorites/favorites.module.ts @@ -0,0 +1,64 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { NgModule } from '@angular/core'; +import { FavoritesComponent } from './favorites.component'; +import { CommonModule } from '@angular/common'; +import { CoreModule } from '@alfresco/adf-core'; +import { RouterModule, Routes } from '@angular/router'; +import { ContentModule } from '@alfresco/adf-content-services'; +import { DirectivesModule } from '../../directives/directives.module'; +import { AppCommonModule } from '../common/common.module'; +import { AppToolbarModule } from '../toolbar/toolbar.module'; +import { ContextMenuModule } from '../context-menu/context-menu.module'; +import { AppInfoDrawerModule } from '../info-drawer/info.drawer.module'; + +const routes: Routes = [ + { + path: '', + component: FavoritesComponent, + data: { + title: 'APP.BROWSE.FAVORITES.TITLE', + sortingPreferenceKey: 'favorites' + } + } +]; + +@NgModule({ + imports: [ + CommonModule, + CoreModule.forChild(), + RouterModule.forChild(routes), + ContentModule.forChild(), + DirectivesModule, + AppCommonModule, + AppToolbarModule, + ContextMenuModule.forChild(), + AppInfoDrawerModule + ], + declarations: [FavoritesComponent], + exports: [FavoritesComponent] +}) +export class AppFavoritesModule {} diff --git a/src/app/components/layout/layout.module.ts b/src/app/components/layout/layout.module.ts new file mode 100644 index 000000000..4fcbc295a --- /dev/null +++ b/src/app/components/layout/layout.module.ts @@ -0,0 +1,52 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CoreModule } from '@alfresco/adf-core'; +import { LayoutComponent } from './layout.component'; +import { SidenavViewsManagerDirective } from './sidenav-views-manager.directive'; +import { ContentModule } from '@alfresco/adf-content-services'; +import { RouterModule } from '@angular/router'; +import { AppSidenavModule } from '../sidenav/sidenav.module'; +import { AppCommonModule } from '../common/common.module'; +import { AppCurrentUserModule } from '../current-user/current-user.module'; +import { AppSearchInputModule } from '../search/search-input.module'; + +@NgModule({ + imports: [ + CommonModule, + RouterModule, + CoreModule.forChild(), + ContentModule.forChild(), + AppCommonModule, + AppSidenavModule, + AppCurrentUserModule, + AppSearchInputModule + ], + declarations: [LayoutComponent, SidenavViewsManagerDirective], + exports: [LayoutComponent] +}) +export class AppLayoutModule {} diff --git a/src/app/components/login/login.module.ts b/src/app/components/login/login.module.ts index c1e5b1129..120354097 100644 --- a/src/app/components/login/login.module.ts +++ b/src/app/components/login/login.module.ts @@ -27,17 +27,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CoreModule } from '@alfresco/adf-core'; import { LoginComponent } from './login.component'; -import { Routes, RouterModule } from '@angular/router'; - -const routes: Routes = [ - { - path: '', - component: LoginComponent - } -]; @NgModule({ - imports: [CommonModule, CoreModule.forChild(), RouterModule.forChild(routes)], + imports: [CommonModule, CoreModule.forChild()], declarations: [LoginComponent] }) export class AppLoginModule {} diff --git a/src/app/dialogs/node-permissions/node-permissions.dialog.html b/src/app/components/permissions/permission-dialog/node-permissions.dialog.html similarity index 79% rename from src/app/dialogs/node-permissions/node-permissions.dialog.html rename to src/app/components/permissions/permission-dialog/node-permissions.dialog.html index 95ae67afe..8a42b7104 100644 --- a/src/app/dialogs/node-permissions/node-permissions.dialog.html +++ b/src/app/components/permissions/permission-dialog/node-permissions.dialog.html @@ -1,6 +1,6 @@

{{'PERMISSIONS.DIALOG.TITLE' | translate}}
- +