mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACS-4865] setup and enable code coverage for all projects (#3074)
This commit is contained in:
parent
86c0bbb998
commit
9609393d1e
22
README.md
22
README.md
@ -39,6 +39,28 @@ npm start content-ce -- --configuration=adf
|
||||
|
||||
Changing the ADF code results in the recompilation and hot-reloading of the ACA application.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Use standard Angular CLI commands to test the projects:
|
||||
|
||||
```sh
|
||||
ng test <project>
|
||||
```
|
||||
|
||||
### Code Coverage
|
||||
|
||||
The projects are already configured to produce code coverage reports in console and HTML output.
|
||||
|
||||
You can view HTML reports in the `./coverage/<project>` folder.
|
||||
|
||||
When working with unit testing and code coverage improvement, you can run unit tests in the "live reload" mode:
|
||||
|
||||
```sh
|
||||
ng test <project> --watch
|
||||
```
|
||||
|
||||
Upon changing unit tests code, you can track the coverage results either in the console output, or by reloading the HTML report in the browser.
|
||||
|
||||
## Triggering the build to use specific branch of ADF with CI flags
|
||||
|
||||
You can create commits with the intention of running the build pipeline using a specific branch of ADF. To achieve this, you need to add a specific CI flag in your commit message:
|
||||
|
@ -367,6 +367,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/adf-office-services-ext/src/test.ts",
|
||||
"tsConfig": "projects/adf-office-services-ext/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/adf-office-services-ext/karma.conf.js"
|
||||
@ -413,6 +414,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/aca-shared/test.ts",
|
||||
"tsConfig": "projects/aca-shared/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-shared/karma.conf.js"
|
||||
@ -465,6 +467,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/aca-about/src/test.ts",
|
||||
"tsConfig": "projects/aca-about/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-about/karma.conf.js"
|
||||
@ -506,6 +509,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/aca-folder-rules/src/test.ts",
|
||||
"tsConfig": "projects/aca-folder-rules/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-folder-rules/karma.conf.js"
|
||||
@ -554,6 +558,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/aca-content/src/test.ts",
|
||||
"tsConfig": "projects/aca-content/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-content/karma.conf.js",
|
||||
@ -605,6 +610,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/aca-viewer/src/test.ts",
|
||||
"tsConfig": "projects/aca-viewer/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-viewer/karma.conf.js"
|
||||
@ -648,6 +654,7 @@
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"codeCoverage": true,
|
||||
"main": "projects/aca-preview/src/test.ts",
|
||||
"tsConfig": "projects/aca-preview/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/aca-preview/karma.conf.js"
|
||||
|
@ -1,25 +1,22 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
// process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
const { join } = require('path');
|
||||
const { constants } = require('karma');
|
||||
|
||||
module.exports = () => {
|
||||
return {
|
||||
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('karma-coverage'),
|
||||
require('karma-mocha-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
files: [
|
||||
{
|
||||
pattern:
|
||||
'./node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
||||
watched: false
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
'./node_modules/@alfresco/adf-core/bundles/assets/adf-core/i18n/en.json',
|
||||
@ -46,18 +43,36 @@ module.exports = function(config) {
|
||||
'/base/node_modules/@alfresco/adf-content-services/bundles/assets/adf-content-services/i18n/en.json'
|
||||
},
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, 'coverage'),
|
||||
reports: ['html', 'lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
|
||||
coverageReporter: {
|
||||
dir: join(__dirname, './coverage'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
check: {
|
||||
global: {
|
||||
statements: 75,
|
||||
branches: 67,
|
||||
functions: 73,
|
||||
lines: 75
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
reporters: ['mocha', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
logLevel: constants.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['ChromeHeadless'],
|
||||
customLaunchers: {
|
||||
@ -65,19 +80,15 @@ module.exports = function(config) {
|
||||
base: 'Chrome',
|
||||
flags: [
|
||||
'--no-sandbox',
|
||||
// '--headless',
|
||||
'--headless',
|
||||
'--disable-gpu',
|
||||
'--remote-debugging-port=9222'
|
||||
]
|
||||
}
|
||||
},
|
||||
singleRun: true,
|
||||
captureTimeout: 180000,
|
||||
browserDisconnectTimeout: 180000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserNoActivityTimeout: 300000,
|
||||
|
||||
restartOnFileChange: true,
|
||||
// workaround for alfresco-js-api builds
|
||||
webpack: { node: { fs: 'empty' } }
|
||||
});
|
||||
};
|
||||
};
|
||||
|
58
package-lock.json
generated
58
package-lock.json
generated
@ -12916,6 +12916,64 @@
|
||||
"which": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"karma-coverage": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.0.tgz",
|
||||
"integrity": "sha512-gPVdoZBNDZ08UCzdMHHhEImKrw1+PAOQOIiffv1YsvxFhBjqvo/SVXNk4tqn1SYqX0BJZT6S/59zgxiBe+9OuA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"istanbul-lib-coverage": "^3.2.0",
|
||||
"istanbul-lib-instrument": "^5.1.0",
|
||||
"istanbul-lib-report": "^3.0.0",
|
||||
"istanbul-lib-source-maps": "^4.0.1",
|
||||
"istanbul-reports": "^3.0.5",
|
||||
"minimatch": "^3.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"istanbul-lib-coverage": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
|
||||
"integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
|
||||
"dev": true
|
||||
},
|
||||
"istanbul-lib-source-maps": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
|
||||
"integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"istanbul-lib-coverage": "^3.0.0",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"istanbul-reports": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
|
||||
"integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"html-escaper": "^2.0.0",
|
||||
"istanbul-lib-report": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"karma-coverage-istanbul-reporter": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz",
|
||||
|
@ -16,7 +16,6 @@
|
||||
"build.release": "npm run build -- --configuration=production,release",
|
||||
"build-libs": "ng build aca-shared && ng build adf-office-services-ext && ng build aca-about && ng build aca-viewer && ng build aca-preview && ng build aca-folder-rules && ng build aca-content",
|
||||
"test": "ng test",
|
||||
"test:ci": "ng test adf-office-services-ext && ng test content-ce --code-coverage",
|
||||
"lint": "NODE_OPTIONS=--max_old_space_size=4096 ng lint",
|
||||
"update-webdriver": "./scripts/update-webdriver.sh",
|
||||
"e2e": "npm run update-webdriver && protractor $SUITE",
|
||||
@ -102,6 +101,7 @@
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "^6.4.1",
|
||||
"karma-chrome-launcher": "~3.1.1",
|
||||
"karma-coverage": "^2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "^3.0.3",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.0.0",
|
||||
|
@ -1,32 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-about'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/aca-about'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
|
@ -1,32 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-content'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/aca-content'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
||||
import { TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import {
|
||||
TranslationService,
|
||||
@ -44,7 +44,6 @@ import { EffectsModule } from '@ngrx/effects';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { INITIAL_STATE } from '../store/initial-state';
|
||||
import { TranslatePipeMock } from './translate-pipe.directive';
|
||||
import { TranslateServiceMock } from '@alfresco/aca-shared';
|
||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||
|
||||
@NgModule({
|
||||
@ -53,6 +52,7 @@ import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||
HttpClientModule,
|
||||
RouterTestingModule,
|
||||
MaterialModule,
|
||||
TranslateModule.forRoot(),
|
||||
StoreModule.forRoot(
|
||||
{ app: appReducer },
|
||||
{
|
||||
@ -71,7 +71,6 @@ import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||
providers: [
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
{ provide: TranslateService, useClass: TranslateServiceMock },
|
||||
{ provide: TranslatePipe, useClass: TranslatePipeMock },
|
||||
{
|
||||
provide: DiscoveryApiService,
|
||||
|
@ -1,32 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-folder-rules'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/aca-folder-rules'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
|
@ -1,32 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-preview'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/aca-preview'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
|
@ -38,11 +38,11 @@ import { UploadService, NodesApiService, DiscoveryApiService } from '@alfresco/a
|
||||
import { AppState, ClosePreviewAction } from '@alfresco/aca-shared/store';
|
||||
import { PreviewComponent } from './preview.component';
|
||||
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
|
||||
import { ContentApiService, AppHookService, TranslateServiceMock, DocumentBasePageService } from '@alfresco/aca-shared';
|
||||
import { ContentApiService, AppHookService, DocumentBasePageService } from '@alfresco/aca-shared';
|
||||
import { Store, StoreModule } from '@ngrx/store';
|
||||
import { Node, NodePaging, FavoritePaging, SharedLinkPaging, PersonEntry, ResultSetPaging, RepositoryInfo, NodeEntry } from '@alfresco/js-api';
|
||||
import { PreviewModule } from '../preview.module';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
@ -115,6 +115,7 @@ describe('PreviewComponent', () => {
|
||||
NoopAnimationsModule,
|
||||
HttpClientModule,
|
||||
RouterTestingModule,
|
||||
TranslateModule.forRoot(),
|
||||
StoreModule.forRoot(
|
||||
{ app: (state) => state },
|
||||
{
|
||||
@ -133,7 +134,6 @@ describe('PreviewComponent', () => {
|
||||
providers: [
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
{ provide: TranslateService, useClass: TranslateServiceMock },
|
||||
{ provide: DocumentBasePageService, useVale: new DocumentBasePageServiceMock() },
|
||||
{
|
||||
provide: DiscoveryApiService,
|
||||
|
@ -1,33 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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('karma-mocha-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-shared'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/aca-shared'),
|
||||
reports: ['html', 'lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['mocha', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
|
@ -143,12 +143,14 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
|
||||
}
|
||||
|
||||
imageResolver(row: ShareDataRow): string | null {
|
||||
if (isLocked(row.node)) {
|
||||
return 'assets/images/baseline-lock-24px.svg';
|
||||
}
|
||||
if (row) {
|
||||
if (isLocked(row.node)) {
|
||||
return 'assets/images/baseline-lock-24px.svg';
|
||||
}
|
||||
|
||||
if (isLibrary(row.node)) {
|
||||
return 'assets/images/baseline-library_books-24px.svg';
|
||||
if (isLibrary(row.node)) {
|
||||
return 'assets/images/baseline-library_books-24px.svg';
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -181,7 +183,7 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
|
||||
return location.href.includes('viewer:view');
|
||||
}
|
||||
|
||||
onSortingChanged(event) {
|
||||
onSortingChanged(event: any) {
|
||||
this.filterSorting = event.detail.key + '-' + event.detail.direction;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
export const INITIAL_APP_STATE: AppState = {
|
||||
appName: 'Alfresco Content Application',
|
||||
@ -98,6 +99,14 @@ class TestComponent extends PageComponent {
|
||||
constructor(store: Store<AppStore>, extensions: AppExtensionService, content: DocumentBasePageService) {
|
||||
super(store, extensions, content);
|
||||
}
|
||||
|
||||
addSubscription(entry: Subscription) {
|
||||
this.subscriptions.push(entry);
|
||||
}
|
||||
|
||||
getSubscriptions(): Subscription[] {
|
||||
return this.subscriptions;
|
||||
}
|
||||
}
|
||||
|
||||
describe('PageComponent', () => {
|
||||
@ -341,4 +350,66 @@ describe('Info Drawer state', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not resolve custom image', () => {
|
||||
expect(component.imageResolver(null)).toBe(null);
|
||||
});
|
||||
|
||||
it('should resolve custom image for locked node', () => {
|
||||
const row: any = {
|
||||
node: {
|
||||
entry: {
|
||||
isLocked: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(component.imageResolver(row)).toBe('assets/images/baseline-lock-24px.svg');
|
||||
});
|
||||
|
||||
it('should resolve custom image for a library', () => {
|
||||
const row: any = {
|
||||
node: {
|
||||
entry: {
|
||||
nodeType: 'st:site'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(component.imageResolver(row)).toBe('assets/images/baseline-library_books-24px.svg');
|
||||
});
|
||||
|
||||
it('should track elements by action id ', () => {
|
||||
const action: any = { id: 'action1' };
|
||||
expect(component.trackByActionId(0, action)).toBe('action1');
|
||||
});
|
||||
|
||||
it('should track elements by id ', () => {
|
||||
const action: any = { id: 'action1' };
|
||||
expect(component.trackById(0, action)).toBe('action1');
|
||||
});
|
||||
|
||||
it('should track elements by column id ', () => {
|
||||
const action: any = { id: 'action1' };
|
||||
expect(component.trackByColumnId(0, action)).toBe('action1');
|
||||
});
|
||||
|
||||
it('should cleanup subscriptions on destroy', () => {
|
||||
const sub = jasmine.createSpyObj('sub', ['unsubscribe']);
|
||||
|
||||
expect(component.getSubscriptions().length).toBe(0);
|
||||
|
||||
component.addSubscription(sub);
|
||||
expect(component.getSubscriptions().length).toBe(1);
|
||||
|
||||
component.ngOnDestroy();
|
||||
expect(sub.unsubscribe).toHaveBeenCalled();
|
||||
expect(component.getSubscriptions().length).toBe(0);
|
||||
});
|
||||
|
||||
it('should update filter sorting', () => {
|
||||
const event = new CustomEvent('sorting-changed', { detail: { key: 'name', direction: 'asc' } });
|
||||
component.onSortingChanged(event);
|
||||
expect(component.filterSorting).toBe('name-asc');
|
||||
});
|
||||
});
|
||||
|
@ -23,21 +23,21 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { LockedByComponent } from './locked-by.component';
|
||||
|
||||
@Injectable()
|
||||
export class TranslateServiceMock extends TranslateService {
|
||||
constructor() {
|
||||
super(null, null, null, null, null, null, true, null, null);
|
||||
}
|
||||
describe('LockedByComponent', () => {
|
||||
it('should evaluate label text', () => {
|
||||
const component = new LockedByComponent();
|
||||
component.node = {
|
||||
entry: {
|
||||
properties: {
|
||||
'cm:lockOwner': {
|
||||
displayName: 'owner-name'
|
||||
}
|
||||
}
|
||||
} as any
|
||||
};
|
||||
|
||||
get(key: string | Array<string>): Observable<string | any> {
|
||||
return of(key);
|
||||
}
|
||||
|
||||
instant(key: string | Array<string>): string | any {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
expect(component.text).toBe('owner-name');
|
||||
});
|
||||
});
|
@ -45,8 +45,6 @@ export class LockedByComponent {
|
||||
node: NodeEntry;
|
||||
|
||||
get text(): string {
|
||||
return (
|
||||
this.node && this.node.entry.properties && this.node.entry.properties['cm:lockOwner'] && this.node.entry.properties['cm:lockOwner'].displayName
|
||||
);
|
||||
return this.node?.entry?.properties?.['cm:lockOwner']?.displayName;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ import { OpenInAppComponent } from './open-in-app.component';
|
||||
import { initialState, LibTestingModule } from '../../testing/lib-testing-module';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { SharedModule } from '@alfresco/aca-shared';
|
||||
|
||||
describe('OpenInAppComponent', () => {
|
||||
let fixture: ComponentFixture<OpenInAppComponent>;
|
||||
@ -15,16 +18,15 @@ describe('OpenInAppComponent', () => {
|
||||
open: jasmine.createSpy('open')
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [OpenInAppComponent],
|
||||
imports: [LibTestingModule, TranslateModule],
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [LibTestingModule, TranslateModule, SharedModule.forRoot(), MatIconModule, MatIconTestingModule],
|
||||
providers: [
|
||||
provideMockStore({ initialState }),
|
||||
{ provide: MAT_DIALOG_DATA, useValue: { redirectUrl: 'mockRedirectUrl' } },
|
||||
{ provide: MatDialogRef, useValue: mockDialogRef }
|
||||
]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(OpenInAppComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
@ -23,10 +23,51 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ToolbarActionComponent } from './toolbar-action.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ToolbarButtonType } from '../toolbar-button/toolbar-button.component';
|
||||
import { ChangeDetectorRef } from '@angular/core';
|
||||
import { ContentActionType } from '@alfresco/adf-extensions';
|
||||
import { IconModule } from '@alfresco/adf-core';
|
||||
|
||||
describe('ToolbarActionComponent', () => {
|
||||
it('should be defined', () => {
|
||||
expect(ToolbarActionComponent).toBeDefined();
|
||||
let fixture: ComponentFixture<ToolbarActionComponent>;
|
||||
let component: ToolbarActionComponent;
|
||||
let changeDetectorRef: ChangeDetectorRef;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CommonModule, HttpClientModule, TranslateModule.forRoot(), IconModule],
|
||||
providers: [{ provide: ChangeDetectorRef, useValue: { markForCheck() {} } }],
|
||||
declarations: [ToolbarActionComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(ToolbarActionComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
changeDetectorRef = TestBed.inject(ChangeDetectorRef);
|
||||
});
|
||||
|
||||
it('should be icon button by default', () => {
|
||||
expect(component.type).toBe(ToolbarButtonType.ICON_BUTTON);
|
||||
});
|
||||
|
||||
it('should force update UI on check for the viewer', () => {
|
||||
component = new ToolbarActionComponent(changeDetectorRef);
|
||||
const markForCheck = spyOn(changeDetectorRef, 'markForCheck');
|
||||
|
||||
component.actionRef = {
|
||||
id: '-app.viewer',
|
||||
type: ContentActionType.button,
|
||||
actions: {
|
||||
click: 'ON_CLICK'
|
||||
}
|
||||
};
|
||||
|
||||
component.ngDoCheck();
|
||||
expect(markForCheck).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -23,10 +23,69 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ToolbarButtonComponent } from './toolbar-button.component';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ToolbarButtonComponent, ToolbarButtonType } from './toolbar-button.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IconModule, TranslationMock, TranslationService } from '@alfresco/adf-core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { of } from 'rxjs';
|
||||
import { AppExtensionService } from '../../../services/app.extension.service';
|
||||
import { ContentActionType } from '@alfresco/adf-extensions';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
describe('ToolbarButtonComponent', () => {
|
||||
it('should be defined', () => {
|
||||
expect(ToolbarButtonComponent).toBeDefined();
|
||||
let fixture: ComponentFixture<ToolbarButtonComponent>;
|
||||
let component: ToolbarButtonComponent;
|
||||
let appExtensionService: AppExtensionService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CommonModule, HttpClientModule, TranslateModule.forRoot(), IconModule, MatButtonModule],
|
||||
declarations: [ToolbarButtonComponent],
|
||||
providers: [
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
{ provide: AppExtensionService, useValue: { runActionById() {} } },
|
||||
{
|
||||
provide: Store,
|
||||
useValue: {
|
||||
dispatch: () => {},
|
||||
select: () => of({ count: 1 })
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(ToolbarButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
appExtensionService = TestBed.inject(AppExtensionService);
|
||||
});
|
||||
|
||||
it('should be icon button by default', () => {
|
||||
expect(component.type).toBe(ToolbarButtonType.ICON_BUTTON);
|
||||
});
|
||||
|
||||
it('should run action on click', async () => {
|
||||
const runActionById = spyOn(appExtensionService, 'runActionById');
|
||||
|
||||
component.actionRef = {
|
||||
id: 'button1',
|
||||
type: ContentActionType.button,
|
||||
actions: {
|
||||
click: 'ON_CLICK'
|
||||
}
|
||||
};
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const button: HTMLButtonElement = fixture.nativeElement.querySelector('[id="button1"]');
|
||||
button.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(runActionById).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -23,10 +23,98 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ToolbarMenuItemComponent } from './toolbar-menu-item.component';
|
||||
import { AppExtensionService } from '../../../services/app.extension.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { IconModule, TranslationMock, TranslationService } from '@alfresco/adf-core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { of } from 'rxjs';
|
||||
import { ContentActionRef, ContentActionType } from '@alfresco/adf-extensions';
|
||||
|
||||
describe('ToolbarMenuItemComponent', () => {
|
||||
it('should be defined', () => {
|
||||
expect(ToolbarMenuItemComponent).toBeDefined();
|
||||
let fixture: ComponentFixture<ToolbarMenuItemComponent>;
|
||||
let component: ToolbarMenuItemComponent;
|
||||
let appExtensionService: AppExtensionService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CommonModule, HttpClientModule, TranslateModule.forRoot(), IconModule, MatButtonModule],
|
||||
declarations: [ToolbarMenuItemComponent],
|
||||
providers: [
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
{ provide: AppExtensionService, useValue: { runActionById() {} } },
|
||||
{
|
||||
provide: Store,
|
||||
useValue: {
|
||||
dispatch: () => {},
|
||||
select: () => of({ count: 1 })
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(ToolbarMenuItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
appExtensionService = TestBed.inject(AppExtensionService);
|
||||
});
|
||||
|
||||
it('should run action on click', async () => {
|
||||
const runActionById = spyOn(appExtensionService, 'runActionById');
|
||||
|
||||
component.actionRef = {
|
||||
id: 'button1',
|
||||
type: ContentActionType.button,
|
||||
actions: {
|
||||
click: 'ON_CLICK'
|
||||
}
|
||||
};
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const button: HTMLButtonElement = fixture.nativeElement.querySelector('[id="button1"]');
|
||||
button.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(runActionById).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should run action with focus selector on click', async () => {
|
||||
const runActionById = spyOn(appExtensionService, 'runActionById');
|
||||
|
||||
component.menuId = 'menu1';
|
||||
component.actionRef = {
|
||||
id: 'button1',
|
||||
type: ContentActionType.button,
|
||||
actions: {
|
||||
click: 'ON_CLICK'
|
||||
}
|
||||
};
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const button: HTMLButtonElement = fixture.nativeElement.querySelector('[id="button1"]');
|
||||
button.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(runActionById).toHaveBeenCalledWith('ON_CLICK', { focusedElementOnCloseSelector: '#menu1' });
|
||||
});
|
||||
|
||||
it('should track elements by content action id', () => {
|
||||
const contentActionRef: ContentActionRef = {
|
||||
id: 'action1',
|
||||
type: ContentActionType.button
|
||||
};
|
||||
|
||||
expect(component.trackByActionId(0, contentActionRef)).toBe('action1');
|
||||
});
|
||||
});
|
||||
|
@ -28,7 +28,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MaterialModule } from '@alfresco/adf-core';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core';
|
||||
import { ContentActionRef } from '@alfresco/adf-extensions';
|
||||
import { ContentActionRef, ContentActionType } from '@alfresco/adf-extensions';
|
||||
import { QueryList } from '@angular/core';
|
||||
|
||||
describe('ToolbarMenuComponent', () => {
|
||||
let fixture: ComponentFixture<ToolbarMenuComponent>;
|
||||
@ -50,6 +51,7 @@ describe('ToolbarMenuComponent', () => {
|
||||
component = fixture.componentInstance;
|
||||
component.matTrigger = jasmine.createSpyObj('MatMenuTrigger', ['closeMenu']);
|
||||
component.actionRef = actions;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@ -59,4 +61,23 @@ describe('ToolbarMenuComponent', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.matTrigger.closeMenu).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should populate underlying menu with toolbar items', () => {
|
||||
component.toolbarMenuItems = new QueryList();
|
||||
component.toolbarMenuItems.reset([{ menuItem: {} } as any]);
|
||||
expect(component.toolbarMenuItems.length).toBe(1);
|
||||
|
||||
expect(component.menu._allItems.length).toBe(0);
|
||||
component.ngAfterViewInit();
|
||||
expect(component.menu._allItems.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should track elements by content action id', () => {
|
||||
const contentActionRef: ContentActionRef = {
|
||||
id: 'action1',
|
||||
type: ContentActionType.button
|
||||
};
|
||||
|
||||
expect(component.trackByActionId(0, contentActionRef)).toBe('action1');
|
||||
});
|
||||
});
|
||||
|
51
projects/aca-shared/src/lib/routing/shared.guard.spec.ts
Normal file
51
projects/aca-shared/src/lib/routing/shared.guard.spec.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { AppSharedRuleGuard } from './shared.guard';
|
||||
|
||||
describe('AppSharedRuleGuard', () => {
|
||||
it('should allow activation if quick share is enabled', () => {
|
||||
const store: any = {
|
||||
select: () => of(true)
|
||||
};
|
||||
const guard = new AppSharedRuleGuard(store);
|
||||
const emittedSpy = jasmine.createSpy('emitted');
|
||||
|
||||
guard.canActivate({} as any).subscribe(emittedSpy);
|
||||
expect(emittedSpy).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should allow child activation if quick share is enabled', () => {
|
||||
const store: any = {
|
||||
select: () => of(true)
|
||||
};
|
||||
const guard = new AppSharedRuleGuard(store);
|
||||
const emittedSpy = jasmine.createSpy('emitted');
|
||||
|
||||
guard.canActivateChild({} as any).subscribe(emittedSpy);
|
||||
expect(emittedSpy).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
@ -39,11 +39,11 @@ export class AppSharedRuleGuard implements CanActivate {
|
||||
this.isQuickShareEnabled$ = store.select(isQuickShareEnabled);
|
||||
}
|
||||
|
||||
canActivate(_: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||
canActivate(_: ActivatedRouteSnapshot): Observable<boolean> {
|
||||
return this.isQuickShareEnabled$;
|
||||
}
|
||||
|
||||
canActivateChild(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||
canActivateChild(route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||
return this.canActivate(route);
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,13 @@ import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { AlfrescoOfficeExtensionService } from './alfresco-office-extension.service';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState, LibTestingModule } from '../testing/lib-testing-module';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
describe('AlfrescoOfficeExtensionService', () => {
|
||||
let appConfig: AppConfigService;
|
||||
let service: AlfrescoOfficeExtensionService;
|
||||
let onLoad$: Subject<any>;
|
||||
|
||||
const mock = () => {
|
||||
let storage: { [key: string]: any } = {};
|
||||
return {
|
||||
@ -48,16 +51,44 @@ describe('AlfrescoOfficeExtensionService', () => {
|
||||
providers: [provideMockStore({ initialState })]
|
||||
});
|
||||
|
||||
service = TestBed.inject(AlfrescoOfficeExtensionService);
|
||||
onLoad$ = new Subject();
|
||||
|
||||
appConfig = TestBed.inject(AppConfigService);
|
||||
appConfig.config = Object.assign(appConfig.config, {
|
||||
aosPlugin: true
|
||||
});
|
||||
appConfig.onLoad = onLoad$;
|
||||
appConfig.config.aosPlugin = true;
|
||||
|
||||
service = TestBed.inject(AlfrescoOfficeExtensionService);
|
||||
|
||||
Object.defineProperty(window, 'localStorage', { value: mock() });
|
||||
});
|
||||
|
||||
it('should enable plugin on load', () => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue(null);
|
||||
spyOn(localStorage, 'setItem');
|
||||
|
||||
onLoad$.next({
|
||||
plugins: {
|
||||
aosPlugin: true
|
||||
}
|
||||
});
|
||||
|
||||
TestBed.inject(AlfrescoOfficeExtensionService);
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('aosPlugin', 'true');
|
||||
});
|
||||
|
||||
it('should disable plugin on load', () => {
|
||||
spyOn(localStorage, 'removeItem');
|
||||
|
||||
onLoad$.next({
|
||||
plugins: {
|
||||
aosPlugin: false
|
||||
}
|
||||
});
|
||||
|
||||
TestBed.inject(AlfrescoOfficeExtensionService);
|
||||
expect(localStorage.removeItem).toHaveBeenCalledWith('aosPlugin');
|
||||
});
|
||||
|
||||
it('Should initialize the localStorage with the item aosPlugin true if not present', () => {
|
||||
expect(localStorage.getItem('aosPlugin')).toBeNull('The localStorage aosPlugin is not null');
|
||||
service.enablePlugin();
|
||||
|
@ -39,15 +39,20 @@ import {
|
||||
ExtensionConfig,
|
||||
NavBarGroupRef
|
||||
} from '@alfresco/adf-extensions';
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { hasQuickShareEnabled } from '@alfresco/aca-shared/rules';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
describe('AppExtensionService', () => {
|
||||
let service: AppExtensionService;
|
||||
let store: Store<AppStore>;
|
||||
let extensions: ExtensionService;
|
||||
let appConfigService: AppConfigService;
|
||||
let logService: LogService;
|
||||
let iconRegistry: MatIconRegistry;
|
||||
let sanitizer: DomSanitizer;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -55,6 +60,8 @@ describe('AppExtensionService', () => {
|
||||
providers: [provideMockStore({ initialState })]
|
||||
});
|
||||
|
||||
iconRegistry = TestBed.inject(MatIconRegistry);
|
||||
sanitizer = TestBed.inject(DomSanitizer);
|
||||
appConfigService = TestBed.inject(AppConfigService);
|
||||
store = TestBed.inject(Store);
|
||||
|
||||
@ -62,6 +69,7 @@ describe('AppExtensionService', () => {
|
||||
service.repository.status.isQuickShareEnabled = true;
|
||||
|
||||
extensions = TestBed.inject(ExtensionService);
|
||||
logService = TestBed.inject(LogService);
|
||||
});
|
||||
|
||||
const applyConfig = (config: ExtensionConfig, selection?: boolean) => {
|
||||
@ -78,6 +86,40 @@ describe('AppExtensionService', () => {
|
||||
};
|
||||
|
||||
describe('configs', () => {
|
||||
it('should log an error during setup', async () => {
|
||||
spyOn(extensions, 'load').and.returnValue(Promise.resolve(null));
|
||||
spyOn(logService, 'error').and.stub();
|
||||
|
||||
await service.load();
|
||||
expect(service.config).toBeNull();
|
||||
expect(logService.error).toHaveBeenCalledWith('Extension configuration not found');
|
||||
});
|
||||
|
||||
it('should load content metadata presets', () => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
features: {
|
||||
'content-metadata-presets': [
|
||||
{
|
||||
id: 'app.content.metadata.kitten-images',
|
||||
'kitten-images': {
|
||||
id: 'app.content.metadata.kittenAspect',
|
||||
'custom:aspect': '*',
|
||||
'exif:exif': ['exif:pixelXDimension', 'exif:pixelYDimension']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
expect(service.contentMetadata).toBeDefined();
|
||||
});
|
||||
|
||||
it('should merge two arrays based on [id] keys', () => {
|
||||
const left = [
|
||||
{
|
||||
@ -1074,6 +1116,107 @@ describe('AppExtensionService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('rules', () => {
|
||||
it('should evaluate rule', () => {
|
||||
extensions.setEvaluators({
|
||||
rule1: () => true
|
||||
});
|
||||
|
||||
expect(service.evaluateRule('rule1')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should not evaluate missing rule and return [false] by default', () => {
|
||||
expect(service.evaluateRule('missing')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should confirm the rule is defined', () => {
|
||||
extensions.setEvaluators({
|
||||
rule1: () => true
|
||||
});
|
||||
|
||||
expect(service.isRuleDefined('rule1')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should not confirm the rule is defined', () => {
|
||||
expect(service.isRuleDefined(null)).toBeFalse();
|
||||
expect(service.isRuleDefined('')).toBeFalse();
|
||||
expect(service.isRuleDefined('missing')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should allow node preview', () => {
|
||||
extensions.setEvaluators({
|
||||
'app.canPreview': () => true
|
||||
});
|
||||
|
||||
service.viewerRules.canPreview = 'app.canPreview';
|
||||
expect(service.canPreviewNode(null)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should allow node preview with no rules', () => {
|
||||
service.viewerRules = {};
|
||||
expect(service.canPreviewNode(null)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should not allow node preview', () => {
|
||||
extensions.setEvaluators({
|
||||
'app.canPreview': () => false
|
||||
});
|
||||
|
||||
service.viewerRules.canPreview = 'app.canPreview';
|
||||
expect(service.canPreviewNode(null)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should allow viewer navigation', () => {
|
||||
extensions.setEvaluators({
|
||||
'app.allowNavigation': () => true
|
||||
});
|
||||
|
||||
service.viewerRules.showNavigation = 'app.allowNavigation';
|
||||
expect(service.canShowViewerNavigation(null)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should allow viewer navigation with no rules', () => {
|
||||
service.viewerRules.showNavigation = null;
|
||||
expect(service.canShowViewerNavigation(null)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should not allow viewer navigation', () => {
|
||||
extensions.setEvaluators({
|
||||
'app.allowNavigation': () => false
|
||||
});
|
||||
|
||||
service.viewerRules.showNavigation = 'app.allowNavigation';
|
||||
expect(service.canShowViewerNavigation(null)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should confirm the viewer extension is disabled explicitly', () => {
|
||||
const extension = {
|
||||
disabled: true
|
||||
};
|
||||
|
||||
expect(service.isViewerExtensionDisabled(extension)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should confirm the viewer extension is disabled via rules', () => {
|
||||
extensions.setEvaluators({
|
||||
'viewer.disabled': () => true
|
||||
});
|
||||
|
||||
const extension = {
|
||||
disabled: false,
|
||||
rules: {
|
||||
disabled: 'viewer.disabled'
|
||||
}
|
||||
};
|
||||
|
||||
expect(service.isViewerExtensionDisabled(extension)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should confirm viewer extension is not disabled by default', () => {
|
||||
expect(service.isViewerExtensionDisabled({})).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rule disable', () => {
|
||||
beforeEach(() => {
|
||||
extensions.setEvaluators({
|
||||
@ -1425,4 +1568,107 @@ describe('AppExtensionService', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom icons', () => {
|
||||
it('should register custom icons', () => {
|
||||
spyOn(iconRegistry, 'addSvgIconInNamespace').and.stub();
|
||||
|
||||
const rawUrl = './assets/images/ft_ic_ms_excel.svg';
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
features: {
|
||||
icons: [
|
||||
{
|
||||
id: 'adf:excel_thumbnail',
|
||||
value: rawUrl
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const url = sanitizer.bypassSecurityTrustResourceUrl(rawUrl);
|
||||
expect(iconRegistry.addSvgIconInNamespace).toHaveBeenCalledWith('adf', 'excel_thumbnail', url);
|
||||
});
|
||||
|
||||
it('should warn if icon has no url path', () => {
|
||||
const warn = spyOn(logService, 'warn').and.stub();
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
features: {
|
||||
icons: [
|
||||
{
|
||||
id: 'adf:excel_thumbnail'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
expect(warn).toHaveBeenCalledWith('Missing icon value for "adf:excel_thumbnail".');
|
||||
});
|
||||
|
||||
it('should warn if icon has incorrect format', () => {
|
||||
const warn = spyOn(logService, 'warn').and.stub();
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
features: {
|
||||
icons: [
|
||||
{
|
||||
id: 'incorrect.format',
|
||||
value: './assets/images/ft_ic_ms_excel.svg'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
expect(warn).toHaveBeenCalledWith(`Incorrect icon id format.`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve main action', (done) => {
|
||||
extensions.setEvaluators({
|
||||
'action.enabled': () => true
|
||||
});
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
features: {
|
||||
mainAction: {
|
||||
id: 'action-id',
|
||||
title: 'action-title',
|
||||
type: 'button',
|
||||
rules: {
|
||||
visible: 'action.enabled'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
service.getMainAction().subscribe((action) => {
|
||||
expect(action.id).toEqual('action-id');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -196,9 +196,9 @@ export class AppExtensionService implements RuleContext {
|
||||
const value = icon.value;
|
||||
|
||||
if (!value) {
|
||||
console.warn(`Missing icon value for "${icon.id}".`);
|
||||
this.logger.warn(`Missing icon value for "${icon.id}".`);
|
||||
} else if (!ns || !id) {
|
||||
console.warn(`Incorrect icon id format: "${icon.id}".`);
|
||||
this.logger.warn(`Incorrect icon id format.`);
|
||||
} else {
|
||||
this.matIconRegistry.addSvgIconInNamespace(ns, id, this.sanitizer.bypassSecurityTrustResourceUrl(value));
|
||||
}
|
||||
@ -290,12 +290,10 @@ export class AppExtensionService implements RuleContext {
|
||||
let presets = {};
|
||||
presets = this.filterDisabled(mergeObjects(presets, ...elements));
|
||||
|
||||
try {
|
||||
this.appConfig.config['content-metadata'].presets = presets;
|
||||
} catch (error) {
|
||||
this.logger.error(error, '- could not change content-metadata presets from app.config -');
|
||||
}
|
||||
const metadata = this.appConfig.config['content-metadata'] || {};
|
||||
metadata.presets = presets;
|
||||
|
||||
this.appConfig.config['content-metadata'] = metadata;
|
||||
return { presets };
|
||||
}
|
||||
|
||||
@ -310,11 +308,7 @@ export class AppExtensionService implements RuleContext {
|
||||
.filter((entry) => this.filterVisible(entry))
|
||||
.sort(sortByOrder);
|
||||
|
||||
try {
|
||||
this.appConfig.config['search'] = search;
|
||||
} catch (error) {
|
||||
this.logger.error(error, '- could not change search from app.config -');
|
||||
}
|
||||
this.appConfig.config['search'] = search;
|
||||
return search;
|
||||
}
|
||||
|
||||
|
@ -30,60 +30,49 @@ import {
|
||||
AppConfigService,
|
||||
AlfrescoApiService,
|
||||
PageTitleService,
|
||||
UserPreferencesService,
|
||||
AlfrescoApiServiceMock,
|
||||
TranslationMock,
|
||||
TranslationService
|
||||
} from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { SharedLinksApiService, GroupService, SearchQueryBuilderService, UploadService, DiscoveryApiService } from '@alfresco/adf-content-services';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ContentApiService } from './content-api.service';
|
||||
import { RouterExtensionService } from './router.extension.service';
|
||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||
import { AppStore, STORE_INITIAL_APP_DATA } from '../../../store/src/states/app.state';
|
||||
import { MockStore, provideMockStore } from '@ngrx/store/testing';
|
||||
import {
|
||||
DiscoveryApiService,
|
||||
FileUploadErrorEvent,
|
||||
GroupService,
|
||||
SearchQueryBuilderService,
|
||||
SharedLinksApiService,
|
||||
UploadService
|
||||
} from '@alfresco/adf-content-services';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { STORE_INITIAL_APP_DATA } from '../../../store/src/states/app.state';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TranslateServiceMock } from '../testing/translation.service';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { RepositoryInfo } from '@alfresco/js-api';
|
||||
import { AcaMobileAppSwitcherService } from './aca-mobile-app-switcher.service';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { SnackbarErrorAction } from '../../../store/src/actions/snackbar.actions';
|
||||
import { ContentApiService } from './content-api.service';
|
||||
import { SetRepositoryInfoAction, SetUserProfileAction } from '../../../store/src/actions/app.actions';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
let auth: AuthenticationService;
|
||||
let appConfig: AppConfigService;
|
||||
let searchQueryBuilderService: SearchQueryBuilderService;
|
||||
let userPreferencesService: UserPreferencesService;
|
||||
let router: Router;
|
||||
let activatedRoute: ActivatedRoute;
|
||||
let routerExtensionService: RouterExtensionService;
|
||||
let pageTitleService: PageTitleService;
|
||||
let uploadService: UploadService;
|
||||
let contentApiService: ContentApiService;
|
||||
let store: Store;
|
||||
let sharedLinksApiService: SharedLinksApiService;
|
||||
let overlayContainer: OverlayContainer;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
let contentApi: ContentApiService;
|
||||
let groupService: GroupService;
|
||||
let storeInitialAppData: any;
|
||||
let store: MockStore<AppStore>;
|
||||
let acaMobileAppSwitcherService: AcaMobileAppSwitcherService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientModule, RouterTestingModule.withRoutes([]), MatDialogModule],
|
||||
imports: [CommonModule, HttpClientModule, TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), MatDialogModule],
|
||||
providers: [
|
||||
CommonModule,
|
||||
SearchQueryBuilderService,
|
||||
UserPreferencesService,
|
||||
RouterExtensionService,
|
||||
UploadService,
|
||||
ContentApiService,
|
||||
SharedLinksApiService,
|
||||
OverlayContainer,
|
||||
provideMockStore({}),
|
||||
{
|
||||
provide: PageTitleService,
|
||||
@ -118,48 +107,19 @@ describe('AppService', () => {
|
||||
isLoggedIn: () => false
|
||||
}
|
||||
},
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
{ provide: TranslateService, useClass: TranslateServiceMock }
|
||||
{ provide: TranslationService, useClass: TranslationMock }
|
||||
]
|
||||
});
|
||||
|
||||
appConfig = TestBed.inject(AppConfigService);
|
||||
searchQueryBuilderService = TestBed.inject(SearchQueryBuilderService);
|
||||
userPreferencesService = TestBed.inject(UserPreferencesService);
|
||||
router = TestBed.inject(Router);
|
||||
activatedRoute = TestBed.inject(ActivatedRoute);
|
||||
routerExtensionService = TestBed.inject(RouterExtensionService);
|
||||
pageTitleService = TestBed.inject(PageTitleService);
|
||||
uploadService = TestBed.inject(UploadService);
|
||||
contentApiService = TestBed.inject(ContentApiService);
|
||||
sharedLinksApiService = TestBed.inject(SharedLinksApiService);
|
||||
overlayContainer = TestBed.inject(OverlayContainer);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
groupService = TestBed.inject(GroupService);
|
||||
storeInitialAppData = TestBed.inject(STORE_INITIAL_APP_DATA);
|
||||
store = TestBed.inject(MockStore);
|
||||
auth = TestBed.inject(AuthenticationService);
|
||||
acaMobileAppSwitcherService = TestBed.inject(AcaMobileAppSwitcherService);
|
||||
|
||||
service = new AppService(
|
||||
userPreferencesService,
|
||||
auth,
|
||||
store,
|
||||
router,
|
||||
activatedRoute,
|
||||
appConfig,
|
||||
pageTitleService,
|
||||
alfrescoApiService,
|
||||
uploadService,
|
||||
routerExtensionService,
|
||||
contentApiService,
|
||||
sharedLinksApiService,
|
||||
groupService,
|
||||
overlayContainer,
|
||||
storeInitialAppData,
|
||||
searchQueryBuilderService,
|
||||
acaMobileAppSwitcherService
|
||||
);
|
||||
searchQueryBuilderService = TestBed.inject(SearchQueryBuilderService);
|
||||
uploadService = TestBed.inject(UploadService);
|
||||
store = TestBed.inject(Store);
|
||||
sharedLinksApiService = TestBed.inject(SharedLinksApiService);
|
||||
contentApi = TestBed.inject(ContentApiService);
|
||||
groupService = TestBed.inject(GroupService);
|
||||
service = TestBed.inject(AppService);
|
||||
});
|
||||
|
||||
it('should be ready if [withCredentials] mode is used', (done) => {
|
||||
@ -169,26 +129,7 @@ describe('AppService', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const instance = new AppService(
|
||||
userPreferencesService,
|
||||
auth,
|
||||
store,
|
||||
router,
|
||||
activatedRoute,
|
||||
appConfig,
|
||||
pageTitleService,
|
||||
alfrescoApiService,
|
||||
uploadService,
|
||||
routerExtensionService,
|
||||
contentApiService,
|
||||
sharedLinksApiService,
|
||||
groupService,
|
||||
overlayContainer,
|
||||
storeInitialAppData,
|
||||
searchQueryBuilderService,
|
||||
acaMobileAppSwitcherService
|
||||
);
|
||||
|
||||
const instance = TestBed.inject(AppService);
|
||||
expect(instance.withCredentials).toBeTruthy();
|
||||
|
||||
instance.ready$.subscribe(() => {
|
||||
@ -204,4 +145,98 @@ describe('AppService', () => {
|
||||
auth.onLogin.next();
|
||||
await expect(isReady).toEqual(true);
|
||||
});
|
||||
|
||||
it('should reset search to defaults upon logout', async () => {
|
||||
const resetToDefaults = spyOn(searchQueryBuilderService, 'resetToDefaults');
|
||||
auth.onLogout.next(true);
|
||||
|
||||
await expect(resetToDefaults).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should rase notification on share link error', () => {
|
||||
spyOn(store, 'select').and.returnValue(of(''));
|
||||
service.init();
|
||||
const dispatch = spyOn(store, 'dispatch');
|
||||
|
||||
sharedLinksApiService.error.next({ message: 'Error Message', statusCode: 1 });
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('Error Message'));
|
||||
});
|
||||
|
||||
it('should raise notification on upload error', async () => {
|
||||
spyOn(store, 'select').and.returnValue(of(''));
|
||||
service.init();
|
||||
const dispatch = spyOn(store, 'dispatch');
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, { status: 403 }));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.403'));
|
||||
dispatch.calls.reset();
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, { status: 404 }));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.404'));
|
||||
dispatch.calls.reset();
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, { status: 409 }));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.CONFLICT'));
|
||||
dispatch.calls.reset();
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, { status: 500 }));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.500'));
|
||||
dispatch.calls.reset();
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, { status: 504 }));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.504'));
|
||||
dispatch.calls.reset();
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, { status: 403 }));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.403'));
|
||||
dispatch.calls.reset();
|
||||
|
||||
uploadService.fileUploadError.next(new FileUploadErrorEvent(null, {}));
|
||||
expect(dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.UPLOAD.ERROR.GENERIC'));
|
||||
});
|
||||
|
||||
it('should load custom css', () => {
|
||||
const appendChild = spyOn(document.head, 'appendChild');
|
||||
spyOn(store, 'select').and.returnValue(of('/custom.css'));
|
||||
service.init();
|
||||
|
||||
const cssLinkElement = document.createElement('link');
|
||||
cssLinkElement.setAttribute('rel', 'stylesheet');
|
||||
cssLinkElement.setAttribute('type', 'text/css');
|
||||
cssLinkElement.setAttribute('href', '/custom.css');
|
||||
|
||||
expect(appendChild).toHaveBeenCalledWith(cssLinkElement);
|
||||
});
|
||||
|
||||
it('should load repository status on login', () => {
|
||||
const repository: any = {};
|
||||
spyOn(contentApi, 'getRepositoryInformation').and.returnValue(of({ entry: { repository } }));
|
||||
spyOn(store, 'select').and.returnValue(of(''));
|
||||
service.init();
|
||||
|
||||
const dispatch = spyOn(store, 'dispatch');
|
||||
auth.onLogin.next(true);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(new SetRepositoryInfoAction(repository));
|
||||
});
|
||||
|
||||
it('should load user profile on login', async () => {
|
||||
const person: any = { id: 'person' };
|
||||
|
||||
const group: any = { entry: {} };
|
||||
const groups: any[] = [group];
|
||||
|
||||
spyOn(contentApi, 'getRepositoryInformation').and.returnValue(of({} as any));
|
||||
spyOn(groupService, 'listAllGroupMembershipsForPerson').and.returnValue(Promise.resolve(groups));
|
||||
spyOn(contentApi, 'getPerson').and.returnValue(of({ entry: person }));
|
||||
|
||||
spyOn(store, 'select').and.returnValue(of(''));
|
||||
service.init();
|
||||
|
||||
const dispatch = spyOn(store, 'dispatch');
|
||||
auth.onLogin.next(true);
|
||||
|
||||
await expect(groupService.listAllGroupMembershipsForPerson).toHaveBeenCalled();
|
||||
await expect(dispatch).toHaveBeenCalledWith(new SetUserProfileAction({ person, groups: [group.entry] }));
|
||||
});
|
||||
});
|
||||
|
@ -23,13 +23,13 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Inject, Injectable, OnDestroy } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { AuthenticationService, AppConfigService, AlfrescoApiService, PageTitleService, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { Observable, BehaviorSubject, Subject } from 'rxjs';
|
||||
import { Observable, BehaviorSubject } from 'rxjs';
|
||||
import { GroupService, SearchQueryBuilderService, SharedLinksApiService, UploadService, FileUploadErrorEvent } from '@alfresco/adf-content-services';
|
||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||
import { ActivatedRoute, ActivationEnd, NavigationStart, Router } from '@angular/router';
|
||||
import { filter, map, takeUntil, tap } from 'rxjs/operators';
|
||||
import { filter, map, tap } from 'rxjs/operators';
|
||||
import {
|
||||
AppState,
|
||||
AppStore,
|
||||
@ -54,7 +54,7 @@ import { AcaMobileAppSwitcherService } from './aca-mobile-app-switcher.service';
|
||||
providedIn: 'root'
|
||||
})
|
||||
// After moving shell to ADF to core, AppService will implement ShellAppService
|
||||
export class AppService implements OnDestroy {
|
||||
export class AppService {
|
||||
private ready: BehaviorSubject<boolean>;
|
||||
ready$: Observable<boolean>;
|
||||
|
||||
@ -63,8 +63,6 @@ export class AppService implements OnDestroy {
|
||||
hideSidenavConditions = ['/preview/'];
|
||||
minimizeSidenavConditions = ['search'];
|
||||
|
||||
onDestroy$ = new Subject<boolean>();
|
||||
|
||||
/**
|
||||
* Whether `withCredentials` mode is enabled.
|
||||
* Usually means that `Kerberos` mode is used.
|
||||
@ -110,11 +108,6 @@ export class AppService implements OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
init(): void {
|
||||
this.alfrescoApiService.getInstance().on('error', (error: { status: number; response: any }) => {
|
||||
if (error.status === 401 && !this.alfrescoApiService.isExcludedErrorListener(error?.response?.req?.url)) {
|
||||
@ -146,24 +139,21 @@ export class AppService implements OnDestroy {
|
||||
this.store.dispatch(new SetCurrentUrlAction(router.url));
|
||||
});
|
||||
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter((event) => event instanceof NavigationStart),
|
||||
takeUntil(this.onDestroy$)
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(new ResetSelectionAction());
|
||||
});
|
||||
this.router.events.pipe(filter((event) => event instanceof NavigationStart)).subscribe(() => {
|
||||
this.store.dispatch(new ResetSelectionAction());
|
||||
});
|
||||
|
||||
this.routerExtensionService.mapExtensionRoutes();
|
||||
|
||||
this.uploadService.fileUploadError.subscribe((error) => this.onFileUploadedError(error));
|
||||
|
||||
this.sharedLinksApiService.error.pipe(takeUntil(this.onDestroy$)).subscribe((err: { message: string }) => {
|
||||
this.store.dispatch(new SnackbarErrorAction(err.message));
|
||||
this.sharedLinksApiService.error.subscribe((err: { message: string }) => {
|
||||
if (err?.message) {
|
||||
this.store.dispatch(new SnackbarErrorAction(err.message));
|
||||
}
|
||||
});
|
||||
|
||||
this.ready$.pipe(takeUntil(this.onDestroy$)).subscribe((isReady) => {
|
||||
this.ready$.subscribe((isReady) => {
|
||||
if (isReady) {
|
||||
this.loadRepositoryStatus();
|
||||
this.loadUserProfile();
|
||||
@ -182,7 +172,9 @@ export class AppService implements OnDestroy {
|
||||
|
||||
private loadRepositoryStatus() {
|
||||
this.contentApi.getRepositoryInformation().subscribe((response: DiscoveryEntry) => {
|
||||
this.store.dispatch(new SetRepositoryInfoAction(response.entry.repository));
|
||||
if (response?.entry?.repository) {
|
||||
this.store.dispatch(new SetRepositoryInfoAction(response.entry.repository));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -201,7 +193,7 @@ export class AppService implements OnDestroy {
|
||||
}
|
||||
|
||||
loadAppSettings() {
|
||||
let baseShareUrl = this.config.get<string>('baseShareUrl');
|
||||
let baseShareUrl = this.config.get<string>('baseShareUrl', '');
|
||||
if (!baseShareUrl.endsWith('/')) {
|
||||
baseShareUrl += '/';
|
||||
}
|
||||
@ -224,23 +216,23 @@ export class AppService implements OnDestroy {
|
||||
onFileUploadedError(error: FileUploadErrorEvent) {
|
||||
let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC';
|
||||
|
||||
if (error.error.status === 403) {
|
||||
if (error?.error?.status === 403) {
|
||||
message = 'APP.MESSAGES.UPLOAD.ERROR.403';
|
||||
}
|
||||
|
||||
if (error.error.status === 404) {
|
||||
if (error?.error?.status === 404) {
|
||||
message = 'APP.MESSAGES.UPLOAD.ERROR.404';
|
||||
}
|
||||
|
||||
if (error.error.status === 409) {
|
||||
if (error?.error?.status === 409) {
|
||||
message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT';
|
||||
}
|
||||
|
||||
if (error.error.status === 500) {
|
||||
if (error?.error?.status === 500) {
|
||||
message = 'APP.MESSAGES.UPLOAD.ERROR.500';
|
||||
}
|
||||
|
||||
if (error.error.status === 504) {
|
||||
if (error?.error?.status === 504) {
|
||||
message = 'APP.MESSAGES.UPLOAD.ERROR.504';
|
||||
}
|
||||
|
||||
|
@ -61,61 +61,61 @@ import { map } from 'rxjs/operators';
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ContentApiService {
|
||||
_nodesApi: NodesApi;
|
||||
private _nodesApi: NodesApi;
|
||||
get nodesApi(): NodesApi {
|
||||
this._nodesApi = this._nodesApi ?? new NodesApi(this.api.getInstance());
|
||||
return this._nodesApi;
|
||||
}
|
||||
|
||||
_trashcanApi: TrashcanApi;
|
||||
private _trashcanApi: TrashcanApi;
|
||||
get trashcanApi(): TrashcanApi {
|
||||
this._trashcanApi = this._trashcanApi ?? new TrashcanApi(this.api.getInstance());
|
||||
return this._trashcanApi;
|
||||
}
|
||||
|
||||
_sharedLinksApi: SharedlinksApi;
|
||||
private _sharedLinksApi: SharedlinksApi;
|
||||
get sharedLinksApi(): SharedlinksApi {
|
||||
this._sharedLinksApi = this._sharedLinksApi ?? new SharedlinksApi(this.api.getInstance());
|
||||
return this._sharedLinksApi;
|
||||
}
|
||||
|
||||
_discoveryApi: DiscoveryApi;
|
||||
private _discoveryApi: DiscoveryApi;
|
||||
get discoveryApi(): DiscoveryApi {
|
||||
this._discoveryApi = this._discoveryApi ?? new DiscoveryApi(this.api.getInstance());
|
||||
return this._discoveryApi;
|
||||
}
|
||||
|
||||
_favoritesApi: FavoritesApi;
|
||||
private _favoritesApi: FavoritesApi;
|
||||
get favoritesApi(): FavoritesApi {
|
||||
this._favoritesApi = this._favoritesApi ?? new FavoritesApi(this.api.getInstance());
|
||||
return this._favoritesApi;
|
||||
}
|
||||
|
||||
_contentApi: ContentApi;
|
||||
private _contentApi: ContentApi;
|
||||
get contentApi(): ContentApi {
|
||||
this._contentApi = this._contentApi ?? new ContentApi(this.api.getInstance());
|
||||
return this._contentApi;
|
||||
}
|
||||
|
||||
_sitesApi: SitesApi;
|
||||
private _sitesApi: SitesApi;
|
||||
get sitesApi(): SitesApi {
|
||||
this._sitesApi = this._sitesApi ?? new SitesApi(this.api.getInstance());
|
||||
return this._sitesApi;
|
||||
}
|
||||
|
||||
_searchApi: SearchApi;
|
||||
private _searchApi: SearchApi;
|
||||
get searchApi(): SearchApi {
|
||||
this._searchApi = this._searchApi ?? new SearchApi(this.api.getInstance());
|
||||
return this._searchApi;
|
||||
}
|
||||
|
||||
_peopleApi: PeopleApi;
|
||||
private _peopleApi: PeopleApi;
|
||||
get peopleApi(): PeopleApi {
|
||||
this._peopleApi = this._peopleApi ?? new PeopleApi(this.api.getInstance());
|
||||
return this._peopleApi;
|
||||
}
|
||||
|
||||
_versionsApi: VersionsApi;
|
||||
private _versionsApi: VersionsApi;
|
||||
get versionsApi(): VersionsApi {
|
||||
this._versionsApi = this._versionsApi ?? new VersionsApi(this.api.getInstance());
|
||||
return this._versionsApi;
|
||||
|
@ -26,16 +26,21 @@
|
||||
import { Node } from '@alfresco/js-api';
|
||||
|
||||
export function isLocked(node: { entry: Node }): boolean {
|
||||
const { entry } = node;
|
||||
if (node?.entry) {
|
||||
const { entry } = node;
|
||||
|
||||
return (
|
||||
(entry && entry.isLocked) ||
|
||||
(entry.properties && (entry.properties['cm:lockType'] === 'READ_ONLY_LOCK' || entry.properties['cm:lockType'] === 'WRITE_LOCK'))
|
||||
);
|
||||
return entry.isLocked || entry.properties?.['cm:lockType'] === 'READ_ONLY_LOCK' || entry.properties?.['cm:lockType'] === 'WRITE_LOCK';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isLibrary(node: { entry: Node | any }): boolean {
|
||||
const { entry } = node;
|
||||
if (node?.entry) {
|
||||
const { entry } = node;
|
||||
|
||||
return (entry.guid && entry.id && entry.preset && entry.title && entry.visibility) || entry.nodeType === 'st:site';
|
||||
return !!(entry.guid && entry.id && entry.preset && entry.title && entry.visibility) || entry.nodeType === 'st:site';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
115
projects/aca-shared/src/lib/utils/note.utils.spec.ts
Normal file
115
projects/aca-shared/src/lib/utils/note.utils.spec.ts
Normal file
@ -0,0 +1,115 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { isLibrary, isLocked } from './node.utils';
|
||||
|
||||
describe('NodeUtils', () => {
|
||||
describe('isLocked', () => {
|
||||
it('should return [false] if entry is not defined', () => {
|
||||
expect(isLocked(null)).toBeFalse();
|
||||
expect(isLocked({ entry: null })).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return [true] if entry is locked', () => {
|
||||
expect(
|
||||
isLocked({
|
||||
entry: {
|
||||
isLocked: true
|
||||
} as any
|
||||
})
|
||||
).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return [true] for [READ_ONLY_LOCK] type', () => {
|
||||
expect(
|
||||
isLocked({
|
||||
entry: {
|
||||
isLocked: false,
|
||||
properties: {
|
||||
'cm:lockType': 'READ_ONLY_LOCK'
|
||||
}
|
||||
} as any
|
||||
})
|
||||
).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return [true] for [WRITE_LOCK] type', () => {
|
||||
expect(
|
||||
isLocked({
|
||||
entry: {
|
||||
isLocked: false,
|
||||
properties: {
|
||||
'cm:lockType': 'WRITE_LOCK'
|
||||
}
|
||||
} as any
|
||||
})
|
||||
).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return [false] for unknown lock type', () => {
|
||||
expect(
|
||||
isLocked({
|
||||
entry: {
|
||||
isLocked: false,
|
||||
properties: {
|
||||
'cm:lockType': 'UNKNOWN'
|
||||
}
|
||||
} as any
|
||||
})
|
||||
).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLibrary', () => {
|
||||
it('should return [false] if entry is not defined', () => {
|
||||
expect(isLibrary(null)).toBeFalse();
|
||||
expect(isLibrary({ entry: null })).toBeFalse();
|
||||
});
|
||||
|
||||
it('should detect library by [st:site] node type', () => {
|
||||
expect(
|
||||
isLibrary({
|
||||
entry: {
|
||||
nodeType: 'st:site'
|
||||
}
|
||||
})
|
||||
).toBeTrue();
|
||||
});
|
||||
|
||||
it('should detect library by common properties', () => {
|
||||
expect(
|
||||
isLibrary({
|
||||
entry: {
|
||||
guid: '<guid>',
|
||||
id: '<id>',
|
||||
preset: '<preset>',
|
||||
title: '<title>',
|
||||
visibility: '<visibility>'
|
||||
}
|
||||
})
|
||||
).toBeTrue();
|
||||
});
|
||||
});
|
||||
});
|
@ -65,4 +65,3 @@ export * from './lib/services/alfresco-office-extension.service';
|
||||
export * from './lib/utils/node.utils';
|
||||
export * from './lib/shared.module';
|
||||
export * from './lib/testing/lib-testing-module';
|
||||
export * from './lib/testing/translation.service';
|
||||
|
@ -1,32 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-viewer'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/aca-viewer'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
|
@ -1,5 +1,10 @@
|
||||
{
|
||||
"AOS": {
|
||||
"ACTION_TITLE": "Edit in Microsoft Office™"
|
||||
"ACTION_TITLE": "Edit in Microsoft Office™",
|
||||
"ERRORS": {
|
||||
"UNSUPPORTED_PLATFORM": "Only supported for Windows and MacOS platforms",
|
||||
"ALREADY_LOCKED": "Document '{{ nodeId }}' is locked by '{{ lockOwner }}'",
|
||||
"MISSING_PROTOCOL_HANDLER": "No protocol handler found for '{{ nodeName }}'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,15 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const { join } = require('path');
|
||||
const getBaseKarmaConfig = require('../../karma.conf');
|
||||
|
||||
module.exports = function(config) {
|
||||
module.exports = function (config) {
|
||||
const baseConfig = getBaseKarmaConfig();
|
||||
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('karma-mocha-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
...baseConfig,
|
||||
coverageReporter: {
|
||||
...baseConfig.coverageReporter,
|
||||
dir: join(__dirname, '../../coverage/aca-office-services-ext'),
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(
|
||||
__dirname,
|
||||
'../../coverage/adf-office-services-ext'
|
||||
),
|
||||
reports: ['html', 'lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['mocha', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true
|
||||
});
|
||||
};
|
||||
|
@ -0,0 +1,156 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AosEditOnlineService } from './aos-extension.service';
|
||||
import { AppConfigService, AuthenticationService, CoreModule, LogService, NotificationService } from '@alfresco/adf-core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
describe('AosEditOnlineService', () => {
|
||||
let aosEditOnlineService: AosEditOnlineService;
|
||||
let notificationService: NotificationService;
|
||||
let authenticationService: AuthenticationService;
|
||||
let appConfigService: AppConfigService;
|
||||
let userAgent: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule, TranslateModule.forRoot(), CoreModule.forRoot()],
|
||||
providers: [{ provide: LogService, useValue: { error() {} } }]
|
||||
});
|
||||
|
||||
aosEditOnlineService = TestBed.inject(AosEditOnlineService);
|
||||
notificationService = TestBed.inject(NotificationService);
|
||||
authenticationService = TestBed.inject(AuthenticationService);
|
||||
appConfigService = TestBed.inject(AppConfigService);
|
||||
|
||||
spyOn(authenticationService, 'getEcmUsername').and.returnValue('user1');
|
||||
spyOn(appConfigService, 'get').and.returnValue('http://localhost:3000');
|
||||
userAgent = spyOnProperty(navigator, 'userAgent').and.returnValue('mac');
|
||||
});
|
||||
|
||||
it('should raise error if file is already locked by another user', () => {
|
||||
const showError = spyOn(notificationService, 'showError').and.stub();
|
||||
const node: any = {
|
||||
id: 'node1',
|
||||
isFile: true,
|
||||
isLocked: true,
|
||||
properties: {
|
||||
'cm:lockType': 'WRITE_LOCK',
|
||||
'cm:lockOwner': { id: 'user2' }
|
||||
}
|
||||
};
|
||||
|
||||
aosEditOnlineService.onActionEditOnlineAos(node);
|
||||
expect(showError).toHaveBeenCalledWith(`AOS.ERRORS.ALREADY_LOCKED`, null, { nodeId: 'node1', lockOwner: 'user2' });
|
||||
});
|
||||
|
||||
it('should open document if locked by the owner', () => {
|
||||
const openByUrl = spyOn(aosEditOnlineService, 'openByUrl').and.stub();
|
||||
|
||||
const node: any = {
|
||||
id: 'node1',
|
||||
name: 'file.docx',
|
||||
isFile: true,
|
||||
isLocked: true,
|
||||
properties: {
|
||||
'cm:lockType': 'READ_ONLY_LOCK',
|
||||
'cm:lockOwner': { id: 'user1' }
|
||||
}
|
||||
};
|
||||
|
||||
aosEditOnlineService.onActionEditOnlineAos(node);
|
||||
// eslint-disable-next-line @cspell/spellchecker
|
||||
expect(openByUrl).toHaveBeenCalledWith('ms-word', 'http://localhost:3000/Company Home/_aos_nodeid/node1/file.docx');
|
||||
});
|
||||
|
||||
it('should open document for node with 1 path segment', () => {
|
||||
const openByUrl = spyOn(aosEditOnlineService, 'openByUrl').and.stub();
|
||||
|
||||
const node: any = {
|
||||
id: 'node1',
|
||||
name: 'file.docx',
|
||||
isFile: true,
|
||||
isLocked: false,
|
||||
path: {
|
||||
elements: [{ name: 'folder1' }]
|
||||
},
|
||||
properties: {}
|
||||
};
|
||||
|
||||
aosEditOnlineService.onActionEditOnlineAos(node);
|
||||
expect(openByUrl).toHaveBeenCalledWith('ms-word', 'http://localhost:3000/file.docx');
|
||||
});
|
||||
|
||||
it('should open document for node with multiple path segments', () => {
|
||||
const openByUrl = spyOn(aosEditOnlineService, 'openByUrl').and.stub();
|
||||
|
||||
const node: any = {
|
||||
id: 'node1',
|
||||
name: 'file.docx',
|
||||
isFile: true,
|
||||
isLocked: false,
|
||||
path: {
|
||||
elements: [{ name: 'parent' }, { name: 'child' }]
|
||||
},
|
||||
properties: {}
|
||||
};
|
||||
|
||||
aosEditOnlineService.onActionEditOnlineAos(node);
|
||||
expect(openByUrl).toHaveBeenCalledWith('ms-word', 'http://localhost:3000/child/_aos_nodeid/node1/file.docx');
|
||||
});
|
||||
|
||||
it('should raise error when protocol handler is not supported', () => {
|
||||
const showError = spyOn(notificationService, 'showError').and.stub();
|
||||
|
||||
const node: any = {
|
||||
id: 'node1',
|
||||
name: 'file.txt',
|
||||
isFile: true,
|
||||
isLocked: false,
|
||||
properties: {}
|
||||
};
|
||||
|
||||
aosEditOnlineService.onActionEditOnlineAos(node);
|
||||
expect(showError).toHaveBeenCalledWith('AOS.ERRORS.MISSING_PROTOCOL_HANDLER', null, { nodeName: 'file.txt' });
|
||||
});
|
||||
|
||||
it('should raise error for unsupported platform', () => {
|
||||
const showError = spyOn(notificationService, 'showError').and.stub();
|
||||
userAgent.and.returnValue('unknown');
|
||||
|
||||
const node: any = {
|
||||
id: 'node1',
|
||||
name: 'file.docx',
|
||||
isFile: true,
|
||||
isLocked: false,
|
||||
properties: {}
|
||||
};
|
||||
|
||||
aosEditOnlineService.onActionEditOnlineAos(node);
|
||||
expect(showError).toHaveBeenCalledWith('AOS.ERRORS.UNSUPPORTED_PLATFORM');
|
||||
});
|
||||
});
|
@ -24,7 +24,7 @@
|
||||
*/
|
||||
|
||||
/* cspell:disable */
|
||||
import { AppConfigService, AuthenticationService, NotificationService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, AuthenticationService, LogService, NotificationService } from '@alfresco/adf-core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
|
||||
import { getFileExtension, supportedExtensions } from '@alfresco/aca-shared/rules';
|
||||
@ -38,9 +38,10 @@ export interface IAosEditOnlineService {
|
||||
})
|
||||
export class AosEditOnlineService implements IAosEditOnlineService {
|
||||
constructor(
|
||||
private alfrescoAuthenticationService: AuthenticationService,
|
||||
private authenticationService: AuthenticationService,
|
||||
private appConfigService: AppConfigService,
|
||||
private notificationService: NotificationService
|
||||
private notificationService: NotificationService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
onActionEditOnlineAos(node: MinimalNodeEntryEntity): void {
|
||||
@ -51,10 +52,10 @@ export class AosEditOnlineService implements IAosEditOnlineService {
|
||||
// );
|
||||
const checkedOut = node.properties['cm:lockType'] === 'WRITE_LOCK' || node.properties['cm:lockType'] === 'READ_ONLY_LOCK';
|
||||
const lockOwner = node.properties['cm:lockOwner'];
|
||||
const differentLockOwner = lockOwner.id !== this.alfrescoAuthenticationService.getEcmUsername();
|
||||
const differentLockOwner = lockOwner.id !== this.authenticationService.getEcmUsername();
|
||||
|
||||
if (checkedOut && differentLockOwner) {
|
||||
this.onAlreadyLockedNotification(node.id, lockOwner);
|
||||
this.onAlreadyLockedNotification(node.id, lockOwner.id);
|
||||
} else {
|
||||
this.triggerEditOnlineAos(node);
|
||||
}
|
||||
@ -77,17 +78,21 @@ export class AosEditOnlineService implements IAosEditOnlineService {
|
||||
}
|
||||
|
||||
private onAlreadyLockedNotification(nodeId: string, lockOwner: string) {
|
||||
this.notificationService.openSnackMessage(`Document ${nodeId} locked by ${lockOwner}`, 3000);
|
||||
this.logService.error('Document already locked by another user');
|
||||
this.notificationService.showError(`AOS.ERRORS.ALREADY_LOCKED`, null, {
|
||||
nodeId,
|
||||
lockOwner
|
||||
});
|
||||
}
|
||||
|
||||
private getProtocolForFileExtension(fileExtension: string) {
|
||||
return supportedExtensions[fileExtension];
|
||||
return fileExtension && supportedExtensions[fileExtension];
|
||||
}
|
||||
|
||||
private triggerEditOnlineAos(node: MinimalNodeEntryEntity): void {
|
||||
const aosHost = this.appConfigService.get('aosHost');
|
||||
let url: string;
|
||||
const pathElements = (node.path.elements || []).map((segment) => segment.name);
|
||||
const pathElements = (node.path?.elements || []).map((segment) => segment.name);
|
||||
|
||||
if (!pathElements.length) {
|
||||
url = `${aosHost}/Company Home/_aos_nodeid/${this.getNodeId(node)}/${encodeURIComponent(node.name)}`;
|
||||
@ -106,23 +111,25 @@ export class AosEditOnlineService implements IAosEditOnlineService {
|
||||
const protocolHandler = this.getProtocolForFileExtension(fileExtension);
|
||||
|
||||
if (protocolHandler === undefined) {
|
||||
this.notificationService.openSnackMessage(`No protocol handler found for {fileExtension}`, 3000);
|
||||
this.logService.error('Protocol handler missing');
|
||||
this.notificationService.showError(`AOS.ERRORS.MISSING_PROTOCOL_HANDLER`, null, { nodeName: node.name });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isWindows() && !this.isMacOs()) {
|
||||
this.notificationService.openSnackMessage('Only supported for Windows and Mac', 3000);
|
||||
this.logService.error('Unsupported platform');
|
||||
this.notificationService.showError('AOS.ERRORS.UNSUPPORTED_PLATFORM');
|
||||
} else {
|
||||
this.aosTryToLaunchOfficeByMsProtocolHandler(protocolHandler, url);
|
||||
this.openByUrl(protocolHandler, url);
|
||||
}
|
||||
}
|
||||
|
||||
private aosTryToLaunchOfficeByMsProtocolHandler(protocolHandler: string, url: string) {
|
||||
const protocolUrl = protocolHandler + ':ofe%7Cu%7C' + url;
|
||||
openByUrl(protocolHandler: string, url: string) {
|
||||
const finalUrl = protocolHandler + ':ofe%7Cu%7C' + url;
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = protocolUrl;
|
||||
iframe.src = finalUrl;
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user