diff --git a/.travis.yml b/.travis.yml index 97ee8db17..dd3e054c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,18 +30,9 @@ jobs: script: - npm run test:ci - bash <(curl -s https://codecov.io/bash) -X gcov - before_deploy: 'npm run build.tomcat' - deploy: - provider: s3 - region: $ARTIFACTS_REGION - access_key_id: $ARTIFACTS_KEY - secret_access_key: $ARTIFACTS_SECRET - bucket: $ARTIFACTS_BUCKET - skip_cleanup: true - local-dir: dist/tomcat - upload-dir: ${TRAVIS_BUILD_NUMBER}/tomcat - on: - repo: Alfresco/alfresco-content-app - branch: development - stage: e2e + name: 'Nginx' script: npm run build && npm run e2e:docker + - stage: e2e + name: 'Tomcat' + script: npm run build.tomcat && npm run docker.tomcat.e2e diff --git a/.vscode/settings.json b/.vscode/settings.json index 84306494e..b4f4f4d5c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,8 @@ { "javascript.preferences.quoteStyle": "single", + "javascript.preferences.importModuleSpecifier": "relative", "typescript.preferences.quoteStyle": "single", + "typescript.preferences.importModuleSpecifier": "relative", "editor.formatOnSave": true, "[html]": { "editor.formatOnSave": false diff --git a/docker/tomcat/Dockerfile b/docker/tomcat/Dockerfile new file mode 100644 index 000000000..ec8e52cb8 --- /dev/null +++ b/docker/tomcat/Dockerfile @@ -0,0 +1,5 @@ +FROM tomcat +LABEL version="1.3" +LABEL maintainer="Denys Vuika " + +COPY ./artifacts/content-app.war /usr/local/tomcat/webapps/content-app.war diff --git a/docker/tomcat/artifacts/.gitignore b/docker/tomcat/artifacts/.gitignore new file mode 100644 index 000000000..ed5861097 --- /dev/null +++ b/docker/tomcat/artifacts/.gitignore @@ -0,0 +1 @@ +*.war diff --git a/docker/tomcat/artifacts/.gitkeep b/docker/tomcat/artifacts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/docker/tomcat/docker-compose.yml b/docker/tomcat/docker-compose.yml new file mode 100644 index 000000000..1e0130b75 --- /dev/null +++ b/docker/tomcat/docker-compose.yml @@ -0,0 +1,95 @@ +version: '3' + +services: + alfresco: + image: alfresco/alfresco-content-repository-community:6.0.7-ga + depends_on: + - postgres + environment: + JAVA_OPTS: ' + -Ddb.driver=org.postgresql.Driver + -Ddb.username=alfresco + -Ddb.password=alfresco + -Ddb.url=jdbc:postgresql://postgres:5432/alfresco + -Dsolr.host=solr6 + -Dsolr.port=8983 + -Dsolr.secureComms=none + -Dsolr.base.url=/solr + -Dindex.subsystem.name=solr6 + -Dshare.host=localhost + -Ddeployment.method=DOCKER_COMPOSE + -Dcsrf.filter.enabled=false + ' + networks: + - internal + ports: + - 8080:8080 #Browser port + + share: + image: alfresco/alfresco-share:6.0.b + depends_on: + - alfresco + environment: + - REPO_HOST=alfresco + - REPO_PORT=8080 + networks: + - internal + ports: + - 8083:8080 + + postgres: + image: postgres:10.1 + environment: + - POSTGRES_PASSWORD=alfresco + - POSTGRES_USER=alfresco + - POSTGRES_DB=alfresco + command: postgres -c max_connections=300 -c log_min_messages=LOG + networks: + - internal + ports: + - 5432:5432 + + solr6: + image: alfresco/alfresco-search-services:1.1.1 + depends_on: + - alfresco + environment: + #Solr needs to know how to register itself with Alfresco + - SOLR_ALFRESCO_HOST=alfresco + - SOLR_ALFRESCO_PORT=8080 + #Alfresco needs to know how to call solr + - SOLR_SOLR_HOST=solr6 + - SOLR_SOLR_PORT=8983 + #Create the default alfresco and archive cores + - SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive + networks: + - internal + ports: + - 8983:8983 #Browser port + + content-app: + image: alfresco/alfresco-content-app:development-latest-tomcat8 + build: . + depends_on: + - alfresco + networks: + - internal + ports: + - 4001:8080 + # volumes: + # - ./app.config.json:/usr/share/nginx/html/app.config.json + # - ./nginx.conf:/etc/nginx/conf.d/default.conf + + proxy: + image: nginx + depends_on: + - content-app + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf + networks: + - internal + ports: + - 4000:80 + +networks: + ? internal diff --git a/docker/tomcat/nginx.conf b/docker/tomcat/nginx.conf new file mode 100644 index 000000000..f0cbdd129 --- /dev/null +++ b/docker/tomcat/nginx.conf @@ -0,0 +1,43 @@ +server { + listen *:80; + + set $allowOriginSite *; + proxy_pass_request_headers on; + proxy_pass_header Set-Cookie; + + location / { + proxy_pass http://content-app:8080; + + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + proxy_redirect off; + proxy_buffering off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass_header Set-Cookie; + } + + location /alfresco/ { + proxy_pass http://alfresco:8080; + + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + proxy_redirect off; + proxy_buffering off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass_header Set-Cookie; + } + + location /share/ { + proxy_pass http://share:8080; + + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + proxy_redirect off; + proxy_buffering off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass_header Set-Cookie; + } +} diff --git a/e2e/configs.ts b/e2e/configs.ts index 59a4d950e..371c4ca91 100755 --- a/e2e/configs.ts +++ b/e2e/configs.ts @@ -29,7 +29,7 @@ export const BROWSER_RESOLUTION_HEIGHT = 800; export const BROWSER_WAIT_TIMEOUT = 10000; // Application configs -export const APP_HOST = 'http://localhost:4000'; +export const USE_HASH_STRATEGY = true; // Repository configs export const REPO_API_HOST = 'http://localhost:8080'; diff --git a/e2e/pages/page.ts b/e2e/pages/page.ts index c9e0a27d6..656bcf5e5 100755 --- a/e2e/pages/page.ts +++ b/e2e/pages/page.ts @@ -31,11 +31,9 @@ import { promise, ExpectedConditions as EC } from 'protractor'; -import { BROWSER_WAIT_TIMEOUT } from './../configs'; +import { BROWSER_WAIT_TIMEOUT, USE_HASH_STRATEGY } from './../configs'; export abstract class Page { - private static USE_HASH_STRATEGY = true; - private locators = { app: by.css('app-root'), layout: by.css('app-layout'), @@ -69,8 +67,8 @@ export abstract class Page { } load(relativeUrl: string = ''): promise.Promise { - const hash = Page.USE_HASH_STRATEGY ? '/#' : ''; - const path = `${hash}${this.url}${relativeUrl}`; + const hash = USE_HASH_STRATEGY ? '/#' : ''; + const path = `${browser.baseUrl}${hash}${this.url}${relativeUrl}`; return browser.get(path); } diff --git a/e2e/suites/authentication/login.test.ts b/e2e/suites/authentication/login.test.ts index ca8e17d82..f2312a18f 100755 --- a/e2e/suites/authentication/login.test.ts +++ b/e2e/suites/authentication/login.test.ts @@ -29,6 +29,7 @@ import { APP_ROUTES } from '../../configs'; import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { navigate } from '../../utilities/browser-utils'; describe('Login', () => { const peopleApi = new RepoClient().people; @@ -128,11 +129,12 @@ describe('Login', () => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); }); - it('redirects to Home Page when navigating to the Login page while already logged in - [C213107]', async () => { + xit('redirects to Home Page when navigating to the Login page while already logged in - [C213107]', async () => { const { username } = johnDoe; await loginPage.loginWith(username); - await browser.get(APP_ROUTES.LOGIN); + + await navigate(APP_ROUTES.LOGIN); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); }); @@ -189,7 +191,7 @@ describe('Login', () => { }); it('unauthenticated user is redirected to Login page - [C213106]', async () => { - await browser.get(APP_ROUTES.PERSONAL_FILES); + await navigate(APP_ROUTES.PERSONAL_FILES); expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.LOGIN); }); diff --git a/e2e/utilities/browser-utils.ts b/e2e/utilities/browser-utils.ts new file mode 100644 index 000000000..e616f1d76 --- /dev/null +++ b/e2e/utilities/browser-utils.ts @@ -0,0 +1,39 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser } from 'protractor'; +import { USE_HASH_STRATEGY } from '../configs'; + +export async function navigate(relativePath: string) { + const path = [ + browser.baseUrl, + browser.baseUrl.endsWith('/') ? '' : '/', + USE_HASH_STRATEGY ? '#' : '', + relativePath.startsWith('/') ? '' : '/', + relativePath + ].join(''); + + return browser.get(path); +} diff --git a/package.json b/package.json index cbd7b57d5..4d8f22435 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,18 @@ "__e2e": "ng e2e", "__wd:update": "webdriver-manager update --gecko=false --versions.chrome=2.38", "wd:update": "webdriver-manager update --gecko=false", - "e2e": "npm run wd:update && protractor protractor.conf.js", + "e2e": "npm run wd:update && protractor --baseUrl=http://localhost:4000", "start:docker": "docker-compose up -d --build && wait-on http://localhost:8080 && wait-on http://localhost:4000", "stop:docker": "docker-compose stop", "e2e:docker": "npm run start:docker && npm run e2e && npm run stop:docker", "spellcheck": "cspell 'src/**/*.ts' 'e2e/**/*.ts' 'projects/**/*.ts'", "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/app/stats.json", "format:check": "prettier --list-different \"src/{app,environments}/**/*{.ts,.js,.css,.scss}\"", - "build.tomcat": "npm run build -- --base-href ./ && mkdir -p dist/tomcat && jar -cvf dist/tomcat/content-app.war -C dist/app/ .", - "start.tomcat": "docker run -v `pwd`/dist/tomcat/content-app.war:/usr/local/tomcat/webapps/content-app.war -it -p 4080:8080 tomcat || exit 0" + "build.tomcat": "npm run build -- --base-href ./ && jar -cvf docker/tomcat/artifacts/content-app.war -C dist/app/ .", + "e2e.tomcat": "npm run wd:update && protractor --baseUrl=http://localhost:4000/content-app/", + "docker.tomcat.start": "cd docker/tomcat && docker-compose up -d --build && wait-on http://localhost:8080 && wait-on http://localhost:4000", + "docker.tomcat.stop": "cd docker/tomcat && docker-compose stop", + "docker.tomcat.e2e": "npm run docker.tomcat.start && npm run e2e.tomcat" }, "private": true, "dependencies": { diff --git a/protractor.conf.js b/protractor.conf.js index aff2ed518..79241fa42 100755 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -2,9 +2,7 @@ // https://github.com/angular/protractor/blob/master/lib/config.ts const path = require('path'); -const { - SpecReporter -} = require('jasmine-spec-reporter'); +const { SpecReporter } = require('jasmine-spec-reporter'); const jasmineReporters = require('jasmine-reporters'); const CDP = require('chrome-remote-interface'); @@ -25,13 +23,11 @@ function rmDir(dirPath) { if (files.length > 0) for (var i = 0; i < files.length; i++) { var filePath = dirPath + '/' + files[i]; - if (fs.statSync(filePath).isFile()) - fs.unlinkSync(filePath); - else - rmDir(filePath); + if (fs.statSync(filePath).isFile()) fs.unlinkSync(filePath); + else rmDir(filePath); } fs.rmdirSync(dirPath); -}; +} exports.config = { allScriptsTimeout: 40000, @@ -56,10 +52,10 @@ exports.config = { browserName: 'chrome', chromeOptions: { prefs: { - 'credentials_enable_service': false, - 'download': { - 'prompt_for_download': false, - 'default_directory': downloadFolder + credentials_enable_service: false, + download: { + prompt_for_download: false, + default_directory: downloadFolder } }, args: ['--incognito', '--headless', '--remote-debugging-port=9222'] @@ -68,58 +64,68 @@ exports.config = { directConnect: true, - baseUrl: 'http://localhost:4000', + // baseUrl: 'http://localhost:4000', framework: 'jasmine2', jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 60000, - print: function () {} + print: function() {} }, - plugins: [{ - package: 'jasmine2-protractor-utils', - disableHTMLReport: false, - disableScreenshot: false, - screenshotOnExpectFailure: true, - screenshotOnSpecFailure: false, - clearFoldersBeforeTest: true, - htmlReportDir: `${projectRoot}/e2e-output/html-report/`, - screenshotPath: `${projectRoot}/e2e-output/screenshots/` - }], + plugins: [ + { + package: 'jasmine2-protractor-utils', + disableHTMLReport: false, + disableScreenshot: false, + screenshotOnExpectFailure: true, + screenshotOnSpecFailure: false, + clearFoldersBeforeTest: true, + htmlReportDir: `${projectRoot}/e2e-output/html-report/`, + screenshotPath: `${projectRoot}/e2e-output/screenshots/` + } + ], onPrepare() { require('ts-node').register({ project: 'e2e/tsconfig.e2e.json' }); - browser.manage().window().setSize(width, height); + browser + .manage() + .window() + .setSize(width, height); - jasmine.getEnv().addReporter(new SpecReporter({ - spec: { - displayStacktrace: true - } - })); + jasmine.getEnv().addReporter( + new SpecReporter({ + spec: { + displayStacktrace: true + } + }) + ); - jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({ - consolidateAll: true, - savePath: `${projectRoot}/e2e-output/junit-report`, - filePrefix: 'results.xml', - useDotNotation: false, - useFullTestName: false, - reportFailedUrl: true - })); + jasmine.getEnv().addReporter( + new jasmineReporters.JUnitXmlReporter({ + consolidateAll: true, + savePath: `${projectRoot}/e2e-output/junit-report`, + filePrefix: 'results.xml', + useDotNotation: false, + useFullTestName: false, + reportFailedUrl: true + }) + ); rmDir(downloadFolder); - CDP().then(client => { + CDP() + .then(client => { client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: downloadFolder - }) + }); }) .catch(err => { - console.log(err) + console.log(err); }); } -} +}; diff --git a/src/app/services/content-management.service.ts b/src/app/services/content-management.service.ts index d2bdfc173..a2657cb0f 100644 --- a/src/app/services/content-management.service.ts +++ b/src/app/services/content-management.service.ts @@ -234,7 +234,7 @@ export class ContentManagementService { }); } - createLibrary(): Observable { + createLibrary(): Observable { const dialogInstance = this.dialogRef.open(LibraryDialogComponent, { width: '400px' }); @@ -243,9 +243,19 @@ export class ContentManagementService { this.store.dispatch(new SnackbarErrorAction(message)); }); - return dialogInstance - .afterClosed() - .pipe(tap(node => this.libraryCreated.next(node))); + return dialogInstance.afterClosed().pipe( + tap(node => { + if (node) { + this.libraryCreated.next(node); + } + }), + map((node: SiteEntry) => { + if (node && node.entry && node.entry.guid) { + return node.entry.guid; + } + return null; + }) + ); } deleteLibrary(id: string): void { diff --git a/src/app/store/effects/library.effects.ts b/src/app/store/effects/library.effects.ts index c5b39b2f6..4bddb284a 100644 --- a/src/app/store/effects/library.effects.ts +++ b/src/app/store/effects/library.effects.ts @@ -74,7 +74,7 @@ export class LibraryEffects { createLibrary$ = this.actions$.pipe( ofType(CREATE_LIBRARY), mergeMap(() => this.content.createLibrary()), - map(node => new NavigateLibraryAction(node.entry.guid)) + map(libraryId => new NavigateLibraryAction(libraryId)) ); @Effect({ dispatch: false })