diff --git a/.angular-cli.json b/.angular-cli.json index 8fbc0f913..a9075f9f2 100644 --- a/.angular-cli.json +++ b/.angular-cli.json @@ -11,6 +11,7 @@ "assets", "favicon-96x96.png", "app.config.json", + "versions.json", { "glob": "**/*", "input": "../node_modules/ng2-alfresco-core/bundles/assets", "output": "./assets/" }, { "glob": "**/*", "input": "../node_modules/ng2-alfresco-datatable/bundles/assets", "output": "./assets/" }, diff --git a/.gitignore b/.gitignore index 57eba63ef..455f8050e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /dist /tmp /out-tsc +/src/versions.json # dependencies /node_modules diff --git a/README.md b/README.md index 0501563e1..d8e014825 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Alfresco Content App +[Public documentation](https://alfresco.github.io/alfresco-content-app/) + This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.4.7. ## Development server @@ -23,6 +25,25 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github. Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). +## Running documentation locally + +For development purposes, you can run and test documentation locally. +That is useful when working in different branches instead of a `master` one. + +Run the following command to install the lightweight development server [wsrv](https://denysvuika.gitlab.io/wsrv/#/): + +```sh +npm install -g wsrv +``` + +Now you can use the next command to serve the documentation folder in the browser: + +```sh +wsrv docs/ -s -l -o +``` + +The browser page is going to automatically reload upon changes. + ## Further help To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/docs/README.md b/docs/README.md index 0501563e1..18e85ecff 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,28 +1,15 @@ # Alfresco Content App -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.4.7. +## Prerequisites -## Development server +- Alfresco Content Services (Community) or Alfresco Content Services 5.2.2 (Enterprise) +- [node.js](https://nodejs.org/en/) 8.9.1 or later -Run `ng start` for a dev server. Navigate to `http://localhost:3000/` (opens by default). -The app will automatically reload if you change any of the source files. +## Building and running locally -## Code scaffolding +Please refer to the [developer docs](/build) to get more details on building and running application on your local machine. -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. +## Using with Docker -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). +The Content App provides a "Dockerfile" and "docker-compose" files to aid in running application in a container. +Please refer to the "[Using with Docker](/docker)" article for more details. diff --git a/docs/build.md b/docs/build.md new file mode 100644 index 000000000..2afeb489b --- /dev/null +++ b/docs/build.md @@ -0,0 +1,49 @@ +# Building from source code + +The Content App is based on [Angular CLI](https://cli.angular.io), and you can use all the commands, generators and blueprints supported by the CLI. + +Use the following commands to clone a copy of the project, install dependencies and run it. + +```sh +git clone https://github.com/Alfresco/alfresco-content-app.git +cd alfresco-content-app +npm install +npm start +``` + +The application run at port 3000 by default, and should automatically open in the default browser once project compilation finishes. + +## Proxy settings + +The Content App provides a proxy configuration for local development server +that allows you to address specific scenarios with CORS and native authentication dialogue. + +You can find settings in the "proxy.conf.js" file in the project root directory. + +

+The proxy settings get automatically applied every time you run the application with "npm start" script. +You must restart the application every time you change the setting values. +

+ +## Running documentation locally + +For development purposes, you can run and test documentation locally. +That is useful when working in different branches instead of a `master` one. + +Run the following command to install the lightweight development server [wsrv](https://denysvuika.gitlab.io/wsrv/#/): + +```sh +npm install -g wsrv +``` + +Now you can use the next command to serve the documentation folder in the browser: + +```sh +wsrv docs/ -s -l -o +``` + +The browser page is going to automatically reload upon changes. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 000000000..922b1fb13 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,111 @@ +# Application Configuration + +The Content Application provides support for a global settings file `app.config.json` that you can use to customise the behaviour of ACA and ADF components. + +## Server settings + +Once the Content Application starts, it needs to know where the Alfresco Content Services (either Community or Enterprise) server is. +The "ecmHost" property allows you to set the address of the server using the dynamic or static format. + +### Dynamic address + +The example below demonstrates the most common dynamic format for development environment: + +```json +{ + "ecmHost": "http://{hostname}{:port}", + ... +} +``` + +The configuration above assumes you are running ACS and Content App on the same server and port +and allows deploying to different servers having the same unified configuration file. + +For example, a proxy server at `localhost:3000` hosting the Content App as the root application, +and `localhost:3000/alfresco` for the ACS repository. + +At runtime, the application is going to automatically substitute the "{hostname}" value with the original hostname. +Optionally it can also use the value of the original port if present, for example, "3000" at local machines, or skip the value for port 80. + +### Static address + +Alternatively, you can provide a static address for the ACS services if necessary: + +```json +{ + "ecmHost": "http://localhost:3000", + ... +} +``` + +## Application settings + +The are many settings you can change to alter the default behaviour of the application. + +### Application Name + +The following block allows you to change the name of the application. + +```json +{ + ..., + "application": { + "name": "Alfresco Example Content Application" + } +} +``` + +The value of the `application.name` key gets appended to every browser tab title at runtime +with the format `[page title] - [application name]`, +for example: "Personal Files - Alfresco Example Content Application". + +### Restricted content + +You can restrict users from uploading certain types of files and folders by setting or extending the list of rules at the "files.excluded" path. + +By default, the application ships with the following rules already predefined: + +```json +{ + ..., + "files": { + "excluded": [ + ".DS_Store", + "desktop.ini", + "thumbs.db", + ".git" + ] + }, + ... +} +``` + +

+You can get more details on the supported rules in the following article: [Upload Service](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/upload.service.md). +

+ +### Pagination settings + +You can change the default settings of the pagination that gets applied to all the document lists in the application. + +```json +{ + ..., + "document-list": { + "supportedPageSizes": [ + 25, + 50, + 100 + ] + }, + ... +} +``` + +## Your custom settings + +You can store any information in the application configuration file, and access it at runtime by utilising the `AppConfigService` service provided by the ADF framework. + +

+Please refer to the [AppConfigService](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/app-config.service.md) documentation to get more details on Application Configuration features and API available. +

diff --git a/docs/cors.md b/docs/cors.md new file mode 100644 index 000000000..192da5af7 --- /dev/null +++ b/docs/cors.md @@ -0,0 +1,21 @@ +# Cross Origin Resource Sharing (CORS) + +## Chrome Workaround + +For the Chrome you can use the following plugin that allows you toggle CORS: +[Allow-Control-Allow-Origin](https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi) + +## Firefox Workaround + +FireFox users can try the following plugin: [CORS Everywhere](https://addons.mozilla.org/en-Gb/firefox/addon/cors-everywhere/) + +## Safari Workaround + +If you are developing or testing with Safari then you can use the "Develop" menu to toggle the CORS mode. +Please note that page must be reloaded every time you change CORS settings. + +![](images/safari-develop-menu.png) + +## See also + +- [Using CORS](https://www.html5rocks.com/en/tutorials/cors/) diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 000000000..8f24330cc --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,88 @@ +# Using with Docker + +

+This article assumes you are familiar with Docker and know how to create images and containers. +

+ +You can create a Docker image to run Alfresco Content App in the container. + +## Building from source code + +You need to run the following commands to build the project from the source code: + +```sh +npm install +npm run build +``` + +That produces a build in the "dist" folder that you can use with a Docker image. + +

+Also, you may need to update the `dist/app.config.json` file with the settings relevant to your scenario. +

+ +## Creating an image + +The Content Application provides a "Dockerfile" file in the repository root. +You can build the image with the following command: + +```sh +docker image build -t content-app . +``` + +## Running image in a container + +To run the image locally, you can use the following command: + +```sh +docker container run -p 80:80 --rm content-app +``` + +Navigate to "http://localhost" to access the running application. + +## Docker Compose file + +You can also use the "docker-compose" file for local development and testing. +To build and run container run the following command in the root project folder: + +```sh +docker-compose up +``` + +To perform a cleanup operation, use the next command: + +```sh +docker-compose down --rmi all +``` + +Navigate to "http://localhost:3000" to access the running application. + +

+Please keep in mind that you should manually build the project every time you want to publish the image or run it locally with the container. +

+ +## Using with local ACS setup + +If you run ACS at port 8080 as a Docker container (typical development configuration), you can use the following command to build the project before creating an image: + +```sh +npm run build:dev +``` + +The command above updates the "dist/app.config.json" file to point the Content App to "http://localhost:8080" upon startup. +Alternatively, you can change the configuration file manually before generating an image. + +So the development workflow, in this case, is going to be: + +```sh +npm run build:dev +docker-compose up +``` + +Navigate to "http://localhost:3000" to access the running application. + +To perform a cleanup operation, use the next command: + +```sh +docker-compose down --rmi all +``` diff --git a/docs/images/safari-develop-menu.png b/docs/images/safari-develop-menu.png new file mode 100644 index 000000000..af2b58d9a Binary files /dev/null and b/docs/images/safari-develop-menu.png differ diff --git a/docs/index.html b/docs/index.html index 2530e9341..4366360a0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -17,6 +17,32 @@ { title: 'Home', path: '/' + }, + { + title: 'Building', + path: 'build' + }, + { + title: 'Docker', + path: 'docker' + }, + { + title: 'Guides', + type: 'dropdown', + items: [ + { + title: 'Building', + path: 'build' + }, + { + title: 'CORS', + path: 'cors' + }, + { + title: 'Configuration', + path: 'configuration' + } + ] } ], icons: [ diff --git a/e2e/components/data-table/data-table.ts b/e2e/components/data-table/data-table.ts index 43040b3d8..3c3fef193 100644 --- a/e2e/components/data-table/data-table.ts +++ b/e2e/components/data-table/data-table.ts @@ -31,11 +31,23 @@ export class DataTable extends Component { `, body: 'table > tbody', - row: 'tr' + row: 'tr', + cell: 'td', + + emptyListContainer: 'td.adf-no-content-container', + emptyFolderDragAndDrop: '.adf-empty-list_template .adf-empty-folder', + + emptyListTitle: 'div .empty-list__title', + emptyListText: 'div .empty-list__text' }; - private head: ElementFinder = this.component.element(by.css(DataTable.selectors.head)); - private body: ElementFinder = this.component.element(by.css(DataTable.selectors.body)); + head: ElementFinder = this.component.element(by.css(DataTable.selectors.head)); + body: ElementFinder = this.component.element(by.css(DataTable.selectors.body)); + cell = by.css(DataTable.selectors.cell); + emptyList: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyListContainer)); + emptyFolderDragAndDrop: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyFolderDragAndDrop)); + emptyListTitle: ElementFinder = this.component.element(by.css(DataTable.selectors.emptyListTitle)); + emptyListText: ElementArrayFinder = this.component.all(by.css(DataTable.selectors.emptyListText)); constructor(ancestor?: ElementFinder) { super(DataTable.selectors.root, ancestor); @@ -46,7 +58,9 @@ export class DataTable extends Component { return browser.wait(EC.presenceOf(this.head), BROWSER_WAIT_TIMEOUT); } - // waitForEmptyState() {} + waitForEmptyState() { + return browser.wait(EC.presenceOf(this.emptyList), BROWSER_WAIT_TIMEOUT); + } // Header/Column methods getColumnHeaders(): ElementArrayFinder { @@ -107,4 +121,37 @@ export class DataTable extends Component { return click.perform(); } + + // empty state methods + isEmptyList(): promise.Promise { + return this.emptyList.isPresent(); + } + + isEmptyWithDragAndDrop(): promise.Promise { + return this.emptyFolderDragAndDrop.isDisplayed(); + } + + getEmptyDragAndDropText(): promise.Promise { + return this.isEmptyWithDragAndDrop() + .then(() => { + return this.emptyFolderDragAndDrop.getText(); + }) + .catch(() => ''); + } + + getEmptyStateTitle(): promise.Promise { + return this.isEmptyList() + .then(() => { + return this.emptyListTitle.getText(); + }) + .catch(() => ''); + } + + getEmptyStateText(): promise.Promise { + return this.isEmptyList() + .then(() => { + return this.emptyListText.getText(); + }) + .catch(() => ''); + } } diff --git a/e2e/components/menu/menu.ts b/e2e/components/menu/menu.ts index 14888204d..536e4b05e 100644 --- a/e2e/components/menu/menu.ts +++ b/e2e/components/menu/menu.ts @@ -54,4 +54,8 @@ export class Menu extends Component { clickMenuItem(label: string): promise.Promise { return this.getItemByLabel(label).click(); } + + isMenuItemPresent(title: string): promise.Promise { + return this.component.element(by.cssContainingText(Menu.selectors.item, title)).isPresent(); + } } diff --git a/e2e/components/toolbar/toolbar-actions.ts b/e2e/components/toolbar/toolbar-actions.ts index bc4c43a26..23939ea84 100644 --- a/e2e/components/toolbar/toolbar-actions.ts +++ b/e2e/components/toolbar/toolbar-actions.ts @@ -36,6 +36,10 @@ export class ToolbarActions extends Component { return this.buttons.count().then(count => (count === 0)); } + isButtonPresent(title: string): promise.Promise { + return this.component.element(by.css(`${ToolbarActions.selectors.button}[title="${title}"]`)).isPresent(); + } + getButtonByLabel(label: string): ElementFinder { return this.component.element(by.cssContainingText(ToolbarActions.selectors.button, label)); } diff --git a/e2e/pages/login-page.ts b/e2e/pages/login-page.ts index d5ca2dc98..b4c1dbfb0 100644 --- a/e2e/pages/login-page.ts +++ b/e2e/pages/login-page.ts @@ -18,6 +18,7 @@ import { browser, ExpectedConditions as EC, promise } from 'protractor'; import { LoginComponent } from '../components/components'; import { Page } from './page'; +import { Utils } from '../utilities/utils'; import { ADMIN_USERNAME, @@ -38,20 +39,29 @@ export class LoginPage extends Page { load(): promise.Promise { return super.load().then(() => { const { submitButton } = this.login; - const hasSumbitButton = EC.presenceOf(submitButton); + const hasSubmitButton = EC.presenceOf(submitButton); - return browser.wait(hasSumbitButton, BROWSER_WAIT_TIMEOUT) - .then(() => browser.executeScript('window.localStorage.clear();')) - .then(() => browser.executeScript('window.sessionStorage.clear();')) - .then(() => browser.driver.manage().deleteAllCookies()); + return browser.wait(hasSubmitButton, BROWSER_WAIT_TIMEOUT) + .then(() => Utils.clearLocalStorage()) + .then(() => browser.manage().deleteAllCookies()); }); } - loginWith(username: string, password: string): promise.Promise { - return this.login.enterCredentials(username, password).submit(); + loginWith(username: string, password?: string): promise.Promise { + const pass = password || username; + return this.login.enterCredentials(username, pass).submit() + .then(() => super.waitForApp()); } loginWithAdmin(): promise.Promise { return this.loginWith(ADMIN_USERNAME, ADMIN_PASSWORD); } + + tryLoginWith(username: string, password?: string): promise.Promise { + const pass = password || username; + return this.login.enterCredentials(username, pass).submit() + .then(() => { + browser.wait(EC.presenceOf(this.login.errorMessage), BROWSER_WAIT_TIMEOUT); + }); + } } diff --git a/e2e/pages/logout-page.ts b/e2e/pages/logout-page.ts index 5a8f6082a..ee2fac198 100644 --- a/e2e/pages/logout-page.ts +++ b/e2e/pages/logout-page.ts @@ -18,6 +18,7 @@ import { promise } from 'protractor'; import { Page } from './page'; import { APP_ROUTES } from '../configs'; +import { Utils } from '../utilities/utils'; export class LogoutPage extends Page { /** @override */ @@ -27,6 +28,8 @@ export class LogoutPage extends Page { /** @override */ load(): promise.Promise { - return super.load(); + return Utils.clearLocalStorage() + .then(() => Utils.clearSessionStorage()) + .then(() => super.load()); } } diff --git a/e2e/pages/page.ts b/e2e/pages/page.ts index 7ec381401..d925f70aa 100644 --- a/e2e/pages/page.ts +++ b/e2e/pages/page.ts @@ -15,18 +15,21 @@ * limitations under the License. */ -import { browser, element, by, ElementFinder, promise } from 'protractor'; +import { browser, element, by, ElementFinder, promise, ExpectedConditions as EC } from 'protractor'; +import { BROWSER_WAIT_TIMEOUT } from './../configs'; export abstract class Page { private static USE_HASH_STRATEGY = false; private locators = { app: by.css('app-root'), + layout: by.css('app-layout'), overlay: by.css('.cdk-overlay-container'), snackBar: by.css('simple-snack-bar') }; public app: ElementFinder = element(this.locators.app); + public layout: ElementFinder = element(this.locators.layout); public overlay: ElementFinder = element(this.locators.overlay); public snackBar: ElementFinder = element(this.locators.snackBar); @@ -43,6 +46,10 @@ export abstract class Page { return browser.get(path); } + waitForApp() { + return browser.wait(EC.presenceOf(this.layout), BROWSER_WAIT_TIMEOUT); + } + refresh(): promise.Promise { return browser.refresh(); } diff --git a/e2e/suites/actions/create-folder.test.ts b/e2e/suites/actions/create-folder.test.ts index c608562cf..f5639049e 100644 --- a/e2e/suites/actions/create-folder.test.ts +++ b/e2e/suites/actions/create-folder.test.ts @@ -24,21 +24,20 @@ import { Utils } from '../../utilities/utils'; import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; describe('Create folder', () => { - const username = 'jane.doe'; - const password = 'jane.doe'; + const username = `user-${Utils.random()}`; - const parent = 'parent-folder'; - const folderName1 = 'my-folder1'; - const folderName2 = 'my-folder2'; + const parent = `parent-${Utils.random()}`; + const folderName1 = `folder-${Utils.random()}`; + const folderName2 = `folder-${Utils.random()}`; const folderDescription = 'description of my folder'; - const duplicateFolderName = 'duplicate-folder-name'; - const nameWithSpaces = ' folder-name '; + const duplicateFolderName = `folder-${Utils.random()}`; + const nameWithSpaces = ` folder-${Utils.random()} `; - const siteName = 'site-private'; + const siteName = `site-private-${Utils.random()}`; const apis = { admin: new RepoClient(), - user: new RepoClient(username, password) + user: new RepoClient(username, username) }; const loginPage = new LoginPage(); @@ -57,14 +56,14 @@ describe('Create folder', () => { } beforeAll(done => { - apis.admin.people.createUser(username, password) + apis.admin.people.createUser(username) .then(() => apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE)) .then(() => apis.admin.nodes.createFolders([ folderName1 ], `Sites/${siteName}/documentLibrary`)) .then(() => apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER)) .then(() => apis.user.nodes.createFolders([ duplicateFolderName ], parent)) - .then(() => loginPage.load()) - .then(() => loginPage.loginWith(username, password)) - .then(done); + .then(() => loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done)); }); beforeEach(done => { @@ -80,9 +79,9 @@ describe('Create folder', () => { afterAll(done => { Promise .all([ + apis.admin.sites.deleteSite(siteName, true), apis.user.nodes.deleteNodes([ parent ]), logoutPage.load() - .then(() => Utils.clearLocalStorage()) ]) .then(done); }); @@ -128,9 +127,7 @@ describe('Create folder', () => { }) .then(() => { apis.user.nodes.getNodeDescription(folderName2) - .then((description) => { - expect(description).toEqual(folderDescription, 'Description is not correct'); - }); + .then((description) => expect(description).toEqual(folderDescription)); }) ); }); diff --git a/e2e/suites/actions/edit-folder.test.ts b/e2e/suites/actions/edit-folder.test.ts index 441ba60fa..2e58b4d07 100644 --- a/e2e/suites/actions/edit-folder.test.ts +++ b/e2e/suites/actions/edit-folder.test.ts @@ -23,24 +23,23 @@ import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-fo import { Utils } from '../../utilities/utils'; describe('Edit folder', () => { - const username = 'john.doe'; - const password = 'john.doe'; + const username = `user-${Utils.random()}`; - const parent = 'parent-folder'; - const folderName = 'my-folder'; + const parent = `parent-${Utils.random()}`; + const folderName = `folder-${Utils.random()}`; const folderDescription = 'my folder description'; - const folderNameToEdit = 'folder-to-be-edited'; - const duplicateFolderName = 'duplicate-folder-name'; + const folderNameToEdit = `folder-${Utils.random()}`; + const duplicateFolderName = `folder-${Utils.random()}`; - const folderNameEdited = 'edited-folder'; + const folderNameEdited = `folder-${Utils.random()}`; const folderDescriptionEdited = 'my folder description edited'; - const siteName = 'site-private'; + const siteName = `site-private-${Utils.random()}`; const apis = { admin: new RepoClient(), - user: new RepoClient(username, password) + user: new RepoClient(username, username) }; const loginPage = new LoginPage(); @@ -53,18 +52,18 @@ describe('Edit folder', () => { beforeAll(done => { Promise .all([ - apis.admin.people.createUser(username, password), + apis.admin.people.createUser(username), apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE) .then(() => apis.admin.nodes.createFolders([ folderName ], `Sites/${siteName}/documentLibrary`)) ]) .then(() => apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER)) .then(() => Promise.all([ apis.user.nodes.createNodeWithProperties( folderName, '', folderDescription, parent ), - apis.user.nodes.createFolders([ folderNameToEdit, duplicateFolderName ], parent), - loginPage.load() + apis.user.nodes.createFolders([ folderNameToEdit, duplicateFolderName ], parent) ])) - .then(() => loginPage.loginWith(username, password)) - .then(done); + .then(() => loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done)); }); beforeEach(done => { @@ -83,20 +82,10 @@ describe('Edit folder', () => { apis.admin.sites.deleteSite(siteName, true), apis.user.nodes.deleteNodes([ parent ]), logoutPage.load() - .then(() => Utils.clearLocalStorage()) ]) .then(done); }); - it('button is enabled when having permissions', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) - .then(() => { - expect(editButton.isEnabled()).toBe(true); - }) - ); - }); - it('dialog UI defaults', () => { personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) .then(() => dataTable.clickOnRowByContainingText(folderName) @@ -111,7 +100,7 @@ describe('Edit folder', () => { ); }); - it('folder properties are modified when pressing OK', () => { + it('properties are modified when pressing OK', () => { personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) .then(() => dataTable.clickOnRowByContainingText(folderNameToEdit) .then(() => editButton.click()) @@ -136,17 +125,6 @@ describe('Edit folder', () => { ); }); - it('button is not displayed when not enough permissions', () => { - const fileLibrariesPage = new BrowsingPage(APP_ROUTES.FILE_LIBRARIES); - - fileLibrariesPage.sidenav.navigateToLinkByLabel('File Libraries') - .then(() => fileLibrariesPage.dataTable.doubleClickOnRowByContainingText(siteName)) - .then(() => fileLibrariesPage.dataTable.clickOnRowByContainingText(folderName)) - .then(() => { - expect(editButton.isPresent()).not.toBe(true, 'edit button is displayed'); - }); - }); - it('with empty folder name', () => { personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) .then(() => dataTable.clickOnRowByContainingText(folderName) diff --git a/e2e/suites/actions/toolbar-single-selection.test.ts b/e2e/suites/actions/toolbar-single-selection.test.ts new file mode 100644 index 000000000..99bf8d0eb --- /dev/null +++ b/e2e/suites/actions/toolbar-single-selection.test.ts @@ -0,0 +1,540 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browser, protractor, promise } from 'protractor'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { APP_ROUTES, SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { RepoClient } from '../../utilities/repo-client/repo-client'; +import { Utils } from '../../utilities/utils'; + +describe('Toolbar actions - single selection : ', () => { + const username = `user-${Utils.random()}`; + const username2 = `user-${Utils.random()}`; + + const fileUser = `file-${Utils.random()}.txt`; + let fileUserId; + + const folderUser = `folder-${Utils.random()}`; + let folderUserId; + + const fileForDelete = `file-${Utils.random()}.txt`; + let fileForDeleteId; + + const folderForDelete = `folder-${Utils.random()}`; + let folderForDeleteId; + + const siteName = `site-private-${Utils.random()}`; + const fileAdmin = `file-${Utils.random()}.txt`; + const folderAdmin = `folder-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, username) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + const { toolbar } = page; + + beforeAll(done => { + apis.admin.people.createUser(username) + .then(() => apis.user.nodes.createFiles([ fileUser ]).then(resp => { fileUserId = resp.data.entry.id; })) + .then(() => apis.user.nodes.createFiles([ fileForDelete ]).then(resp => { fileForDeleteId = resp.data.entry.id; })) + .then(() => apis.user.nodes.createFolders([ folderForDelete ]).then((resp) => { folderForDeleteId = resp.data.entry.id; })) + .then(() => apis.user.nodes.createFolders([ folderUser ]).then(resp => { folderUserId = resp.data.entry.id; })) + .then(() => apis.user.shared.shareFileById(fileUserId)) + .then(() => apis.user.favorites.addFavoriteById('file', fileUserId)) + .then(() => apis.user.favorites.addFavoriteById('folder', folderUserId)) + .then(done); + }); + + afterAll(done => { + Promise.all([ + apis.user.nodes.deleteNodeById(fileUserId), + apis.user.nodes.deleteNodeById(folderUserId), + logoutPage.load() + ]) + .then(done); + }); + + xit(''); + + describe('Personal Files', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + }); + }); + + it('actions are displayed when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderUser) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + }); + + dataTable.clickOnRowByContainingText(fileUser) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + }) + .then(() => browser.$('body').click()); + }); + + it('correct actions appear when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderUser) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + }); + + dataTable.clickOnRowByContainingText(folderUser) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderUser}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderUser}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderUser}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderUser}`); + }) + .then(() => browser.$('body').click()); + }); + }); + + describe('File Libraries', () => { + beforeAll(done => { + apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC) + .then(() => apis.admin.people.createUser(username2)) + .then(() => apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER)) + .then(() => apis.admin.sites.addSiteMember(siteName, username2, SITE_ROLES.SITE_CONSUMER)) + .then(() => apis.admin.nodes.createFiles([ fileAdmin ], `Sites/${siteName}/documentLibrary`)) + .then(() => apis.admin.nodes.createFolders([ folderAdmin ], `Sites/${siteName}/documentLibrary`)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES) + .then(() => dataTable.waitForHeader()) + .then(() => dataTable.doubleClickOnRowByContainingText(siteName)) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + xit(''); + + describe('user is Manager', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileAdmin) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileAdmin}`); + }); + }); + + it('actions are displayed when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderAdmin) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderAdmin}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileAdmin) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileAdmin}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileAdmin}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileAdmin}`); + }); + + dataTable.clickOnRowByContainingText(fileAdmin) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileAdmin}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileAdmin}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileAdmin}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileAdmin}`); + }) + .then(() => browser.$('body').click()); + }); + + it('correct actions appear when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderAdmin) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderAdmin}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderAdmin}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderAdmin}`); + }); + + dataTable.clickOnRowByContainingText(folderAdmin) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderAdmin}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderAdmin}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderAdmin}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderAdmin}`); + }) + .then(() => browser.$('body').click()); + }); + }); + + describe('user is Consumer', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username2)) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileAdmin) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileAdmin}`); + }); + }); + + it('actions are displayed when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderAdmin) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderAdmin}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileAdmin) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileAdmin}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileAdmin}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileAdmin}`); + }); + + dataTable.clickOnRowByContainingText(fileAdmin) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileAdmin}`); + expect(menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${fileAdmin}`); + expect(menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${fileAdmin}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileAdmin}`); + }) + .then(() => browser.$('body').click()); + }); + + it('correct actions appear when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderAdmin) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderAdmin}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderAdmin}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${folderAdmin}`); + }); + + dataTable.clickOnRowByContainingText(folderAdmin) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderAdmin}`); + expect(menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${folderAdmin}`); + expect(menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${folderAdmin}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderAdmin}`); + }) + .then(() => browser.$('body').click()); + }); + }); + }); + + describe('Shared Files', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + }); + + dataTable.clickOnRowByContainingText(fileUser) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + }) + .then(() => browser.$('body').click()); + }); + }); + + describe('Recent Files', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + }); + + dataTable.clickOnRowByContainingText(fileUser) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + }) + .then(() => browser.$('body').click()); + }); + }); + + describe('Favorites', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); + }); + }); + + it('actions are displayed when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderUser) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileUser) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`); + }); + + dataTable.clickOnRowByContainingText(fileUser) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${fileUser}`); + }) + .then(() => browser.$('body').click()); + }); + + it('correct actions appear when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderUser) + .then(() => { + expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); + expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); + expect(toolbar.actions.isButtonPresent('Edit')).toBe(true, `Edit is not displayed for ${folderUser}`); + }); + + dataTable.clickOnRowByContainingText(folderUser) + .then(() => toolbar.actions.openMoreMenu()) + .then(menu => { + expect(menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${folderUser}`); + expect(menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${folderUser}`); + expect(menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${folderUser}`); + expect(menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${folderUser}`); + }) + .then(() => browser.$('body').click()); + }); + }); + + describe('Trash', () => { + beforeAll(done => { + apis.user.nodes.deleteNodeById(fileForDeleteId, false) + .then(() => apis.user.nodes.deleteNodeById(folderForDeleteId, false)) + .then(() => loginPage.load()) + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + Promise.all([ + apis.user.trashcan.permanentlyDelete(fileForDeleteId), + apis.user.trashcan.permanentlyDelete(folderForDeleteId), + logoutPage.load() + ]) + .then(done); + }); + + it('actions are not displayed when no item is selected', () => { + expect(toolbar.actions.isEmpty()).toBe(true, `actions displayed though nothing selected`); + }); + + it('actions are displayed when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileForDelete) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileForDelete}`); + }); + }); + + it('actions are displayed when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderForDelete) + .then(() => { + expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderForDelete}`); + }); + }); + + it('correct actions appear when a file is selected', () => { + dataTable.clickOnRowByContainingText(fileForDelete) + .then(() => { + expect(toolbar.actions.isButtonPresent('Permanently delete')) + .toBe(true, `Permanently delete is not displayed for ${fileForDelete}`); + expect(toolbar.actions.isButtonPresent('Restore')).toBe(true, `Restore is not displayed for ${fileForDelete}`); + }); + }); + + it('correct actions appear when a folder is selected', () => { + dataTable.clickOnRowByContainingText(folderForDelete) + .then(() => { + expect(toolbar.actions.isButtonPresent('Permanently delete')) + .toBe(true, `Permanently delete is displayed for ${folderForDelete}`); + expect(toolbar.actions.isButtonPresent('Restore')).toBe(true, `Restore is not enabled for ${folderForDelete}`); + }); + }); + }); +}); diff --git a/e2e/suites/application/page-titles.test.ts b/e2e/suites/application/page-titles.test.ts index 6906435af..498913ee6 100644 --- a/e2e/suites/application/page-titles.test.ts +++ b/e2e/suites/application/page-titles.test.ts @@ -65,7 +65,6 @@ describe('Page titles', () => { afterAll(done => { logoutPage.load() - .then(() => Utils.clearLocalStorage()) .then(done); }); diff --git a/e2e/suites/authentication/login.test.ts b/e2e/suites/authentication/login.test.ts index 56d804805..80fa54a38 100644 --- a/e2e/suites/authentication/login.test.ts +++ b/e2e/suites/authentication/login.test.ts @@ -27,19 +27,16 @@ describe('Login', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); - const testUser = { - username: 'test.user@alfness', - password: 'test.user' - }; + const testUser = `user-${Utils.random()}@alfness`; const russianUser = { - username: 'пользователь', + username: `пользвате${Utils.random()}`, password: '密碼中國' }; const johnDoe = { - username: 'john.doe', - password: 'john.doe', + username: `user-${Utils.random()}`, + get password() { return this.username; }, firstName: 'John', lastName: 'Doe' }; @@ -47,7 +44,7 @@ describe('Login', () => { beforeAll(done => { Promise .all([ - peopleApi.createUser(testUser.username, testUser.password), + peopleApi.createUser(testUser), peopleApi.createUser(russianUser.username, russianUser.password), peopleApi.createUser(johnDoe.username, johnDoe.password, { firstName: johnDoe.firstName, @@ -71,10 +68,9 @@ describe('Login', () => { describe('with valid credentials', () => { it('navigate to "Personal Files"', () => { - const { username, password } = johnDoe; + const { username } = johnDoe; - loginPage - .loginWith(username, password) + loginPage.loginWith(username) .then(() => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); }); @@ -82,20 +78,17 @@ describe('Login', () => { it(`displays user's name in header`, () => { const { userInfo } = new BrowsingPage(APP_ROUTES.PERSONAL_FILES).header; - const { username, password, firstName, lastName } = johnDoe; + const { username, firstName, lastName } = johnDoe; - loginPage - .loginWith(username, password) + loginPage.loginWith(username) .then(() => { expect(userInfo.name).toEqual(`${firstName} ${lastName}`); }); }); it(`logs in with user having username containing "@"`, () => { - const { username, password } = testUser; - loginPage - .loginWith(username, password) + .loginWith(testUser) .then(() => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); }); @@ -112,10 +105,10 @@ describe('Login', () => { }); it('redirects to Home Page when navigating to the Login page while already logged in', () => { - const { username, password } = johnDoe; + const { username } = johnDoe; loginPage - .loginWith(username, password) + .loginWith(username) .then(() => browser.get(APP_ROUTES.LOGIN) .then(() => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); @@ -124,10 +117,10 @@ describe('Login', () => { }); it('redirects to Personal Files when pressing browser Back while already logged in ', () => { - const { username, password } = johnDoe; + const { username } = johnDoe; loginPage - .loginWith(username, password) + .loginWith(username) .then(() => browser.driver.navigate().back()) .then(() => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); @@ -157,7 +150,7 @@ describe('Login', () => { it('shows error when entering nonexistent user', () => { loginPage - .loginWith('nonexistent-user', 'any-password') + .tryLoginWith('nonexistent-user', 'any-password') .then(() => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.LOGIN); expect(errorMessage.isDisplayed()).toBe(true); @@ -168,7 +161,7 @@ describe('Login', () => { const { username } = johnDoe; loginPage - .loginWith(username, 'incorrect-password') + .tryLoginWith(username, 'incorrect-password') .then(() => { expect(browser.getCurrentUrl()).toContain(APP_ROUTES.LOGIN); expect(errorMessage.isDisplayed()).toBe(true); diff --git a/e2e/suites/authentication/logout.test.ts b/e2e/suites/authentication/logout.test.ts index 6a48edc25..773f961a0 100644 --- a/e2e/suites/authentication/logout.test.ts +++ b/e2e/suites/authentication/logout.test.ts @@ -29,20 +29,17 @@ describe('Logout', () => { const peopleApi = new RepoClient().people; - const johnDoe = { - username: 'john.doe', - password: 'john.doe' - }; + const johnDoe = `user-${Utils.random()}`; beforeAll((done) => { peopleApi - .createUser(johnDoe.username, johnDoe.password) + .createUser(johnDoe) .then(done); }); beforeEach((done) => { loginPage.load() - .then(() => loginPage.loginWith(johnDoe.username, johnDoe.password)) + .then(() => loginPage.loginWith(johnDoe)) .then(done); }); @@ -59,13 +56,6 @@ describe('Logout', () => { }); }); - xit('redirects to Login page when logging out by URL', () => { - browser.get(APP_ROUTES.LOGOUT) - .then(() => { - expect(browser.getCurrentUrl()).toContain(APP_ROUTES.LOGIN); - }); - }); - it('redirects to Login page when pressing browser Back after logout', () => { page.signOut() .then(() => browser.driver.navigate().back()) diff --git a/e2e/suites/list-views/empty-list.test.ts b/e2e/suites/list-views/empty-list.test.ts new file mode 100644 index 000000000..223c8588e --- /dev/null +++ b/e2e/suites/list-views/empty-list.test.ts @@ -0,0 +1,103 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browser, by } from 'protractor'; + +import { APP_ROUTES, SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; + +describe('Empty list views', () => { + const username = `user-${Utils.random()}`; + const password = username; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, password) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable } = page; + + beforeAll(done => { + apis.admin.people.createUser(username) + .then(() => loginPage.load()) + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('empty Personal Files', () => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES) + .then(() => { + expect(dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(dataTable.getEmptyDragAndDropText()).toContain('Drag and drop'); + }); + }); + + it('empty File Libraries', () => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES) + .then(() => { + expect(dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(dataTable.getEmptyStateTitle()).toContain(`You aren't a member of any File Libraries yet`); + expect(dataTable.getEmptyStateText()).toContain('Join sites to upload, view, and share files.'); + }); + }); + + it('empty Shared Files', () => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES) + .then(() => { + expect(dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(dataTable.getEmptyStateTitle()).toContain('No shared files or folders'); + expect(dataTable.getEmptyStateText()).toContain('Items you share using the Share option are shown here.'); + }); + }); + + it('empty Recent Files', () => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES) + .then(() => { + expect(dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(dataTable.getEmptyStateTitle()).toContain('No recent files'); + expect(dataTable.getEmptyStateText()).toContain('Items you upload or edit in the last 30 days are shown here.'); + }); + }); + + it('empty Favorites', () => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES) + .then(() => { + expect(dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(dataTable.getEmptyStateTitle()).toContain('No favorite files or folders'); + expect(dataTable.getEmptyStateText()).toContain('Favorite items that you want to easily find later.'); + }); + }); + + it('empty Trash', () => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH) + .then(() => { + expect(dataTable.isEmptyList()).toBe(true, 'list is not empty'); + expect(dataTable.getEmptyStateTitle()).toContain('Trash is empty'); + expect(dataTable.getEmptyStateText()).toContain('Items you delete are moved to the Trash.'); + expect(dataTable.getEmptyStateText()).toContain('Empty Trash to permanently delete items.'); + }); + }); +}); diff --git a/e2e/suites/list-views/favorites.test.ts b/e2e/suites/list-views/favorites.test.ts new file mode 100644 index 000000000..57e7dd0fe --- /dev/null +++ b/e2e/suites/list-views/favorites.test.ts @@ -0,0 +1,116 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browser, by } from 'protractor'; + +import { APP_ROUTES, SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; + +describe('Favorites', () => { + const username = `user-${Utils.random()}`; + const password = username; + + const siteName = `site-${Utils.random()}`; + const folderName = `folder-${Utils.random()}`; + const fileName1 = `file-${Utils.random()}.txt`; + const fileName2 = `file-${Utils.random()}.txt`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, password) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const favoritesPage = new BrowsingPage(); + const { dataTable } = favoritesPage; + + beforeAll(done => { + apis.admin.people.createUser(username) + .then(() => apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC)) + .then(() => apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER)) + .then(() => apis.admin.nodes.createFiles([ fileName1 ], `Sites/${siteName}/documentLibrary`) + .then(resp => apis.user.favorites.addFavoriteById('file', resp.data.entry.id))) + .then(() => apis.user.nodes.createFolders([ folderName ]) + .then(resp => apis.user.favorites.addFavoriteById('folder', resp.data.entry.id))) + .then(() => apis.user.nodes.createFiles([ fileName2 ], folderName) + .then(resp => apis.user.favorites.addFavoriteById('file', resp.data.entry.id))) + .then(() => loginPage.load()) + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + Promise.all([ + apis.admin.sites.deleteSite(siteName, true), + apis.user.nodes.deleteNodes([ folderName ]), + logoutPage.load() + ]) + .then(done); + }); + + it('has the correct columns', () => { + const labels = [ 'Name', 'Location', 'Size', 'Modified', 'Modified by' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + + expect(dataTable.getColumnHeaders().count()).toBe(5 + 1, 'Incorrect number of columns'); + + elements.forEach((element, index) => { + expect(element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); + }); + + it('displays the favorite files and folders', () => { + expect(dataTable.countRows()).toEqual(3, 'Incorrect number of items displayed'); + expect(dataTable.getRowByContainingText(fileName1).isPresent()).toBe(true, `${fileName1} not displayed`); + expect(dataTable.getRowByContainingText(fileName2).isPresent()).toBe(true, `${fileName2} not displayed`); + expect(dataTable.getRowByContainingText(folderName).isPresent()).toBe(true, `${folderName} not displayed`); + }); + + it('Location column displays the parent folder of the files', () => { + const itemsLocations = { + [fileName1]: siteName, + [fileName2]: folderName, + [folderName]: 'Personal Files' + }; + + dataTable.getRows() + .map((row) => { + return row.all(dataTable.cell).map(cell => cell.getText()); + }) + .then((rowCells) => { + return rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[2]; + return acc; + }, {}); + }) + .then((favoritesList) => { + Object.keys(itemsLocations).forEach((item) => { + expect(favoritesList[item]).toEqual(itemsLocations[item]); + }); + }); + }); + +}); diff --git a/e2e/suites/list-views/file-libraries.test.ts b/e2e/suites/list-views/file-libraries.test.ts new file mode 100644 index 000000000..5c0e07894 --- /dev/null +++ b/e2e/suites/list-views/file-libraries.test.ts @@ -0,0 +1,122 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browser, by } from 'protractor'; + +import { APP_ROUTES, SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; + +describe('File Libraries', () => { + const username = `user-${Utils.random()}`; + const password = username; + + const sitePrivate = `private-${Utils.random()}`; + const siteModerated = `moderated-${Utils.random()}`; + const sitePublic = `public-${Utils.random()}`; + + const adminSite = `admin-${Utils.random()}`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, password) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const fileLibrariesPage = new BrowsingPage(APP_ROUTES.FILE_LIBRARIES); + const { dataTable } = fileLibrariesPage; + + beforeAll(done => { + Promise + .all([ + apis.admin.people.createUser(username), + apis.admin.sites.createSite(sitePublic, SITE_VISIBILITY.PUBLIC), + apis.admin.sites.createSite(siteModerated, SITE_VISIBILITY.MODERATED), + apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE), + apis.admin.sites.createSite(adminSite, SITE_VISIBILITY.PUBLIC) + ]) + .then(() => apis.admin.sites.addSiteMember(sitePublic, username, SITE_ROLES.SITE_CONSUMER)) + .then(() => apis.admin.sites.addSiteMember(siteModerated, username, SITE_ROLES.SITE_MANAGER)) + .then(() => apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONTRIBUTOR)) + .then(() => loginPage.load()) + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + fileLibrariesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + Promise.all([ + apis.admin.sites.deleteSite(sitePublic, true), + apis.admin.sites.deleteSite(siteModerated, true), + apis.admin.sites.deleteSite(sitePrivate, true), + apis.admin.sites.deleteSite(adminSite, true), + logoutPage.load() + ]) + .then(done); + }); + + it('has the correct columns', () => { + const labels = [ 'Title', 'Status' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + + expect(dataTable.getColumnHeaders().count()).toBe(2 + 1, 'Incorrect number of columns'); + + elements.forEach((element, index) => { + expect(element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); + }); + + it('User can see only the sites he is a member of', () => { + const sitesCount = dataTable.countRows(); + + const expectedSites = { + [sitePrivate]: SITE_VISIBILITY.PRIVATE, + [siteModerated]: SITE_VISIBILITY.MODERATED, + [sitePublic]: SITE_VISIBILITY.PUBLIC + }; + + expect(sitesCount).toEqual(3, 'Incorrect number of sites displayed'); + expect(dataTable.getRowByContainingText(adminSite).isPresent()).toBe(false, 'Incorrect site appears in list'); + + dataTable.getRows() + .map((row) => { + return row.all(by.css('td')).map(cell => cell.getText()); + }) + .then((rowCells) => { + return rowCells.reduce((acc, cell) => { + acc[cell[1]] = cell[2].toUpperCase(); + return acc; + }, {}); + }) + .then((sitesList) => { + Object.keys(expectedSites).forEach((site) => { + expect(sitesList[site]).toEqual(expectedSites[site]); + }); + }); + }); + + // it('Site ID is displayed when two sites have the same name', () => { + // // cannot be implemented until ACA-987 is fixed + // }); +}); diff --git a/e2e/suites/list-views/personal-files.test.ts b/e2e/suites/list-views/personal-files.test.ts index efe263793..d2e079c54 100644 --- a/e2e/suites/list-views/personal-files.test.ts +++ b/e2e/suites/list-views/personal-files.test.ts @@ -23,8 +23,8 @@ import { Utils } from '../../utilities/utils'; import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; describe('Personal Files', () => { - const username = 'jane.doe'; - const password = 'jane.doe'; + const username = `user-${Utils.random()}`; + const password = username; const apis = { admin: new RepoClient(), @@ -36,30 +36,27 @@ describe('Personal Files', () => { const personalFilesPage = new BrowsingPage(APP_ROUTES.PERSONAL_FILES); const dataTable = personalFilesPage.dataTable; - const adminContent: NodeContentTree = { - name: 'admin-folder' - }; + const adminFolder = `admin-folder-${Utils.random()}`; - const userContent: NodeContentTree = { - name: 'user-folder', - files: [ 'user-file.txt' ] - }; + const userFolder = `user-folder-${Utils.random()}`; + const userFile = `file-${Utils.random()}.txt`; beforeAll(done => { Promise .all([ apis.admin.people.createUser(username, password), - apis.admin.nodes.createContent(adminContent) + apis.admin.nodes.createFolders([ adminFolder ]) ]) - .then(() => apis.user.nodes.createContent(userContent)) + .then(() => apis.user.nodes.createFolders([ userFolder ])) + .then(() => apis.user.nodes.createFiles([ userFile ], userFolder)) .then(done); }); afterAll(done => { Promise .all([ - apis.admin.nodes.deleteNodes([ adminContent.name ]), - apis.user.nodes.deleteNodes([ userContent.name ]) + apis.admin.nodes.deleteNodes([ adminFolder ]), + apis.user.nodes.deleteNodes([ userFolder ]) ]) .then(done); }); @@ -81,7 +78,6 @@ describe('Personal Files', () => { afterAll(done => { logoutPage.load() - .then(() => Utils.clearLocalStorage()) .then(done); }); @@ -109,18 +105,15 @@ describe('Personal Files', () => { afterAll(done => { logoutPage.load() - .then(() => Utils.clearLocalStorage()) .then(done); }); - it('has the right column count', () => { - expect(dataTable.getColumnHeaders().count()).toBe(5); - }); - - it('has the right columns', () => { + it('has the correct columns', () => { const labels = [ 'Name', 'Size', 'Modified', 'Modified by' ]; const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + expect(dataTable.getColumnHeaders().count()).toBe(4 + 1, 'Incorrect number of columns'); + elements.forEach((element, index) => { expect(element.isPresent()).toBe(true, `"${labels[index]}" is missing`); }); @@ -131,17 +124,17 @@ describe('Personal Files', () => { }); it('has user created content', () => { - expect(dataTable.getRowByContainingText('user-folder').isPresent()) + expect(dataTable.getRowByContainingText(userFolder).isPresent()) .toBe(true); }); it('navigates to folder', () => { const getNodeIdPromise = apis.user.nodes - .getNodeByPath('/user-folder') + .getNodeByPath(`/${userFolder}`) .then(response => response.data.entry.id); const navigatePromise = dataTable - .doubleClickOnRowByContainingText('user-folder') + .doubleClickOnRowByContainingText(userFolder) .then(() => dataTable.waitForHeader()); Promise @@ -153,8 +146,8 @@ describe('Personal Files', () => { expect(browser.getCurrentUrl()) .toContain(nodeId, 'Node ID is not in the URL'); - expect(dataTable.getRowByContainingText('user-file.txt').isPresent()) - .toBe(true, '"user-file.txt" is missing'); + expect(dataTable.getRowByContainingText(userFile).isPresent()) + .toBe(true, 'user file is missing'); }); }); @@ -165,7 +158,7 @@ describe('Personal Files', () => { const { actions } = personalFilesPage.toolbar; dataTable - .clickOnRowByContainingText('user-folder') + .clickOnRowByContainingText(userFolder) .then(() => { expect(actions.isEmpty()).toBe(false, 'Toolbar to be present'); }) diff --git a/e2e/suites/list-views/trash.test.ts b/e2e/suites/list-views/trash.test.ts new file mode 100644 index 000000000..e6cd440c5 --- /dev/null +++ b/e2e/suites/list-views/trash.test.ts @@ -0,0 +1,142 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browser, by } from 'protractor'; + +import { APP_ROUTES, SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { Utils } from '../../utilities/utils'; +import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; + +describe('Trash', () => { + const username = `user-${Utils.random()}`; + const password = username; + + const siteName = `site-${Utils.random()}`; + const folderAdmin = `folder-${Utils.random()}`; + const folderUser = `folder-${Utils.random()}`; + const fileAdmin = `file-${Utils.random()}.txt`; + const fileUser = `file-${Utils.random()}.txt`; + const fileSite = `file-${Utils.random()}.txt`; + + const apis = { + admin: new RepoClient(), + user: new RepoClient(username, password) + }; + + const loginPage = new LoginPage(); + const logoutPage = new LogoutPage(); + const trashPage = new BrowsingPage(); + const { dataTable } = trashPage; + + beforeAll(done => { + apis.admin.people.createUser(username) + // admin: create file -> delete file + .then(() => apis.admin.nodes.createFiles([ fileAdmin ]) + .then((resp) => apis.admin.nodes.deleteNodeById(resp.data.entry.id, false))) + // admin: create folder -> delete folder + .then(() => apis.admin.nodes.createFolders([ folderAdmin ]) + .then((resp) => apis.admin.nodes.deleteNodeById(resp.data.entry.id, false))) + // admin: create site, add user to site, create file + .then(() => apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC)) + .then(() => apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER)) + .then(() => apis.admin.nodes.createFiles([ fileSite ], `Sites/${siteName}/documentLibrary`) + // user: delete file from site + .then(resp => apis.user.nodes.deleteNodeById(resp.data.entry.id, false))) + // user: create file -> delete file + .then(() => apis.user.nodes.createFiles([ fileUser ]) + .then((resp) => apis.user.nodes.deleteNodeById(resp.data.entry.id, false))) + // user: create folder -> delete folder + .then(() => apis.user.nodes.createFolders([ folderUser ]) + .then((resp) => apis.user.nodes.deleteNodeById(resp.data.entry.id, false))) + .then(done); + }); + + afterAll(done => { + apis.admin.sites.deleteSite(siteName).then(done); + }); + + xit(''); + + describe('as admin', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWithAdmin()) + .then(() => trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('has the correct columns', () => { + const labels = [ 'Name', 'Location', 'Size', 'Deleted', 'Deleted by' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + + expect(dataTable.getColumnHeaders().count()).toBe(5 + 1, 'Incorrect number of columns'); + + elements.forEach((element, index) => { + expect(element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); + }); + + it('displays the files and folders deleted by everyone', () => { + expect(dataTable.countRows()).toEqual(5, 'Incorrect number of deleted items displayed'); + + expect(dataTable.getRowByContainingText(fileAdmin).isPresent()).toBe(true, `${fileAdmin} not displayed`); + expect(dataTable.getRowByContainingText(folderAdmin).isPresent()).toBe(true, `${folderAdmin} not displayed`); + expect(dataTable.getRowByContainingText(fileUser).isPresent()).toBe(true, `${fileUser} not displayed`); + expect(dataTable.getRowByContainingText(folderUser).isPresent()).toBe(true, `${folderUser} not displayed`); + expect(dataTable.getRowByContainingText(fileSite).isPresent()).toBe(true, `${fileSite} not displayed`); + }); + }); + + describe('as user', () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(() => trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH)) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('has the correct columns', () => { + const labels = [ 'Name', 'Location', 'Size', 'Deleted', 'Deleted by' ]; + const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); + + expect(dataTable.getColumnHeaders().count()).toBe(5 + 1, 'Incorrect number of columns'); + + elements.forEach((element, index) => { + expect(element.isPresent()).toBe(true, `"${labels[index]}" is missing`); + }); + }); + + it('displays the files and folders deleted by the user', () => { + expect(dataTable.countRows()).toEqual(3, 'Incorrect number of deleted items displayed'); + + expect(dataTable.getRowByContainingText(fileSite).isPresent()).toBe(true, `${fileSite} not displayed`); + expect(dataTable.getRowByContainingText(fileUser).isPresent()).toBe(true, `${fileUser} not displayed`); + expect(dataTable.getRowByContainingText(folderUser).isPresent()).toBe(true, `${folderUser} not displayed`); + }); + }); +}); diff --git a/e2e/suites/navigation/side-navigation.test.ts b/e2e/suites/navigation/side-navigation.test.ts index bf6c8541d..792b4d845 100644 --- a/e2e/suites/navigation/side-navigation.test.ts +++ b/e2e/suites/navigation/side-navigation.test.ts @@ -19,7 +19,6 @@ import { browser } from 'protractor'; import { APP_ROUTES, SIDEBAR_LABELS } from '../../configs'; import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; -import { Utils } from '../../utilities/utils'; describe('Side navigation', () => { const loginPage = new LoginPage(); @@ -38,7 +37,6 @@ describe('Side navigation', () => { afterAll(done => { logoutPage.load() - .then(() => Utils.clearLocalStorage()) .then(done); }); diff --git a/e2e/suites/pagination/pagination.test.ts b/e2e/suites/pagination/pagination.test.ts index 7fb40c359..e510e3973 100644 --- a/e2e/suites/pagination/pagination.test.ts +++ b/e2e/suites/pagination/pagination.test.ts @@ -23,12 +23,11 @@ import { Utils } from '../../utilities/utils'; import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; describe('Pagination', () => { - const username = 'jane.doe'; - const password = 'jane.doe'; + const username = `user-${Utils.random()}`; const apis = { admin: new RepoClient(), - user: new RepoClient(username, password) + user: new RepoClient(username, username) }; const loginPage = new LoginPage(); @@ -36,7 +35,7 @@ describe('Pagination', () => { beforeAll(done => { apis.admin.people - .createUser(username, password) + .createUser(username) .then(done); }); @@ -48,7 +47,7 @@ describe('Pagination', () => { // Generate files const content: NodeContentTree = { - name: 'user-folder', + name: `user-folder-${Utils.random()}`, files: Array(101) .fill('file') .map((name, index): string => `${name}-${index + 1}.txt`) @@ -58,14 +57,13 @@ describe('Pagination', () => { beforeAll(done => { nodesApi.createContent(content) - .then(() => loginPage.load()) - .then(() => loginPage.loginWith(username, password)) - .then(done); + .then(() => loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done)); }); beforeEach(done => { - personalFilesPage - .load() + personalFilesPage.load() .then(() => dataTable.waitForHeader()) .then(() => dataTable.doubleClickOnRowByContainingText(content.name)) .then(() => dataTable.sortByColumn('Name')) @@ -73,9 +71,7 @@ describe('Pagination', () => { }); afterAll(done => { - logoutPage - .load() - .then(() => Utils.clearLocalStorage()) + logoutPage.load() .then(() => nodesApi.deleteNodes([ content.name ])) .then(done); }); diff --git a/e2e/utilities/repo-client/apis/favorites/favorites-api.ts b/e2e/utilities/repo-client/apis/favorites/favorites-api.ts index ced51018d..e2b0c4ee0 100644 --- a/e2e/utilities/repo-client/apis/favorites/favorites-api.ts +++ b/e2e/utilities/repo-client/apis/favorites/favorites-api.ts @@ -40,6 +40,18 @@ export class FavoritesApi extends RepoApi { .catch(this.handleError); } + addFavoriteById(nodeType: string, id: string): Promise { + const data = [{ + target: { + [nodeType]: { + guid: id + } + } + }]; + return this.post(`/people/-me-/favorites`, { data }) + .catch(this.handleError); + } + getFavorite(api: RepoClient, name: string): Promise { return api.nodes.getNodeByPath(name) .then((response) => { diff --git a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts index 227b566d2..e8ed5423e 100644 --- a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts +++ b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts @@ -33,17 +33,17 @@ export class NodesApi extends RepoApi { .catch(this.handleError); } - deleteNodeById(id: string): Promise { + deleteNodeById(id: string, permanent: boolean = true): Promise { return this - .delete(`/nodes/${id}`) + .delete(`/nodes/${id}?permanent=${permanent}`) .catch(this.handleError); } - deleteNodeByPath(path: string): Promise { + deleteNodeByPath(path: string, permanent: boolean = true): Promise { return this .getNodeByPath(path) .then((response: any): string => response.data.entry.id) - .then((id: string): any => this.deleteNodeById(id)) + .then((id: string): any => this.deleteNodeById(id, permanent)) .catch(this.handleError); } @@ -56,10 +56,10 @@ export class NodesApi extends RepoApi { .catch(this.handleError); } - deleteNodes(names: string[], relativePath: string = ''): Promise { + deleteNodes(names: string[], relativePath: string = '', permanent: boolean = true): Promise { const deletions = names .map((name: string): any => { - return this.deleteNodeByPath(`${relativePath}/${name}`); + return this.deleteNodeByPath(`${relativePath}/${name}`, permanent); }); return Promise.all(deletions); diff --git a/e2e/utilities/repo-client/apis/people/people-api.ts b/e2e/utilities/repo-client/apis/people/people-api.ts index 8f05548a1..5b9a6ecec 100644 --- a/e2e/utilities/repo-client/apis/people/people-api.ts +++ b/e2e/utilities/repo-client/apis/people/people-api.ts @@ -35,7 +35,7 @@ export class PeopleApi extends RepoApi { .catch(this.handleError); } - createUser(username: string, password: string, details?: Person): Promise { + createUser(username: string, password?: string, details?: Person): Promise { const person: Person = new Person(username, password, details); const onSuccess = (response) => response; const onError = (response) => { diff --git a/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts b/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts new file mode 100644 index 000000000..b11854c3f --- /dev/null +++ b/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts @@ -0,0 +1,36 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RepoApi } from '../repo-api'; +import { NodesApi } from '../nodes/nodes-api'; +import { RepoClient } from './../../repo-client'; + +export class SharedLinksApi extends RepoApi { + + shareFileById(id: string): Promise { + const data = [{ nodeId: id }]; + + return this.post(`/shared-links`, { data }) + .catch(this.handleError); + } + + getSharedLinks(): Promise { + return this.get(`/shared-links`) + .catch(this.handleError); + } + +} diff --git a/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts b/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts new file mode 100644 index 000000000..cc50b1465 --- /dev/null +++ b/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts @@ -0,0 +1,26 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RepoApi } from '../repo-api'; + +export class TrashcanApi extends RepoApi { + permanentlyDelete(id: string): Promise { + return this + .delete(`/deleted-nodes/${id}`) + .catch(this.handleError); + } +} diff --git a/e2e/utilities/repo-client/repo-client.ts b/e2e/utilities/repo-client/repo-client.ts index 889c4c1f3..2c0327cce 100644 --- a/e2e/utilities/repo-client/repo-client.ts +++ b/e2e/utilities/repo-client/repo-client.ts @@ -21,13 +21,16 @@ import { PeopleApi } from './apis/people/people-api'; import { NodesApi } from './apis/nodes/nodes-api'; import { SitesApi } from './apis/sites/sites-api'; import { FavoritesApi } from './apis/favorites/favorites-api'; +import { SharedLinksApi } from './apis/shared-links/shared-links-api'; +import { TrashcanApi } from './apis/trashcan/trashcan-api'; export class RepoClient { public people: PeopleApi = new PeopleApi(this.auth, this.config); public nodes: NodesApi = new NodesApi(this.auth, this.config); public sites: SitesApi = new SitesApi(this.auth, this.config); public favorites: FavoritesApi = new FavoritesApi(this.auth, this.config); - // public shared: SharedLinksApi = new SharedLinksApi(this.auth, this.config); + public shared: SharedLinksApi = new SharedLinksApi(this.auth, this.config); + public trashcan: TrashcanApi = new TrashcanApi(this.auth, this.config); constructor( private username: string = RepoClientAuth.DEFAULT_USERNAME, diff --git a/e2e/utilities/utils.ts b/e2e/utilities/utils.ts index 0a6072f68..616adb672 100644 --- a/e2e/utilities/utils.ts +++ b/e2e/utilities/utils.ts @@ -27,4 +27,10 @@ export class Utils { static clearLocalStorage(): promise.Promise { return browser.executeScript('window.localStorage.clear();'); } + + // session storage + static clearSessionStorage(): promise.Promise { + return browser.executeScript('window.sessionStorage.clear();'); + } + } diff --git a/package.json b/package.json index 7319ef40a..a3112269b 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,14 @@ "license": "Apache-2.0", "scripts": { "ng": "ng", - "start": "ng serve --open", - "build": "ng build", - "build:prod": "ng build --prod", - "build:dev": "ng build && node postbuild-dev.js", + "start": "npm run server-versions && ng serve --open", + "build": "npm run server-versions && ng build", + "build:prod": "npm run server-versions && ng build --prod", + "build:dev": "npm run server-versions && ng build && node postbuild-dev.js", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", - "start:dist": "wsrv" + "server-versions": "rimraf ./src/versions.json && npm list --depth=0 --json=true --prod=true > ./src/versions.json || exit 0" }, "private": true, "dependencies": { @@ -39,7 +39,6 @@ "ng2-alfresco-viewer": "1.10.0-beta6", "pdfjs-dist": "1.8.557", "rxjs": "5.1.0", - "wsrv": "0.2.2", "zone.js": "^0.8.14" }, "devDependencies": { @@ -62,6 +61,7 @@ "karma-jasmine-html-reporter": "^0.2.2", "node-rest-client": "^3.1.0", "protractor": "^5.1.2", + "rimraf": "2.6.2", "ts-node": "~3.2.0", "tslint": "~5.7.0", "typescript": "~2.3.3" diff --git a/src/app.config.json b/src/app.config.json index 6d3dbe894..80383e6d4 100644 --- a/src/app.config.json +++ b/src/app.config.json @@ -1,9 +1,10 @@ { - "ecmHost": "http://{hostname}:{port}", + "ecmHost": "http://{hostname}{:port}", "application": { "name": "Alfresco Example Content Application", "build": "1234" }, + "logo": "alfresco-logo-white.svg", "languagePicker": false, "document-list": { "supportedPageSizes": [ @@ -20,6 +21,66 @@ ".git" ] }, + "navigation": { + "main": [ + { + "icon": "folder", + "label": "APP.BROWSE.PERSONAL.SIDENAV_LINK.LABEL", + "title": "APP.BROWSE.PERSONAL.SIDENAV_LINK.TOOLTIP", + "disabled": false, + "route": { + "url": "/personal-files" + } + }, + { + "icon": "group_work", + "label": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.LABEL", + "title": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.TOOLTIP", + "disabled": false, + "route": { + "url": "/libraries" + } + } + ], + "secondary": [ + { + "icon": "people", + "label": "APP.BROWSE.SHARED.SIDENAV_LINK.LABEL", + "title": "APP.BROWSE.SHARED.SIDENAV_LINK.TOOLTIP", + "disabled": false, + "route": { + "url": "/shared" + } + }, + { + "icon": "schedule", + "label": "APP.BROWSE.RECENT.SIDENAV_LINK.LABEL", + "title": "APP.BROWSE.RECENT.SIDENAV_LINK.TOOLTIP", + "disabled": false, + "route": { + "url": "/recent-files" + } + }, + { + "icon": "star", + "label": "APP.BROWSE.FAVORITES.SIDENAV_LINK.LABEL", + "title": "APP.BROWSE.FAVORITES.SIDENAV_LINK.TOOLTIP", + "disabled": false, + "route": { + "url": "/favorites" + } + }, + { + "icon": "delete", + "label": "APP.BROWSE.TRASHCAN.SIDENAV_LINK.LABEL", + "title": "APP.BROWSE.TRASHCAN.SIDENAV_LINK.TOOLTIP", + "disabled": false, + "route": { + "url": "/trashcan" + } + } + ] + }, "languages": [ { "key": "de", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9be78dbd3..7cffa948b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -41,6 +41,8 @@ import { HeaderComponent } from './components/header/header.component'; import { CurrentUserComponent } from './components/current-user/current-user.component'; import { SearchComponent } from './components/search/search.component'; import { SidenavComponent } from './components/sidenav/sidenav.component'; +import { AboutComponent } from './components/about/about.component'; +import { LocationLinkComponent } from './components/location-link/location-link.component'; @NgModule({ imports: [ @@ -67,7 +69,9 @@ import { SidenavComponent } from './components/sidenav/sidenav.component'; RecentFilesComponent, SharedFilesComponent, TrashcanComponent, - PreviewComponent + PreviewComponent, + AboutComponent, + LocationLinkComponent ], providers: [ { diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index bad9a9f40..91ae0667d 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -26,6 +26,7 @@ import { LibrariesComponent } from './components/libraries/libraries.component'; import { RecentFilesComponent } from './components/recent-files/recent-files.component'; import { SharedFilesComponent } from './components/shared-files/shared-files.component'; import { TrashcanComponent } from './components/trashcan/trashcan.component'; +import { AboutComponent } from './components/about/about.component'; import { LoginComponent } from './components/login/login.component'; import { PreviewComponent } from './components/preview/preview.component'; @@ -34,13 +35,16 @@ import { GenericErrorComponent } from './components/generic-error/generic-error. export const APP_ROUTES: Routes = [ { path: 'preview/:nodeId', - component: PreviewComponent + component: PreviewComponent, + data: { + i18nTitle: 'APP.PREVIEW.TITLE' + } }, { path: 'login', component: LoginComponent, data: { - title: 'Sign in' + i18nTitle: 'APP.SIGN_IN' } }, { @@ -113,6 +117,13 @@ export const APP_ROUTES: Routes = [ i18nTitle: 'APP.BROWSE.TRASHCAN.TITLE' } }, + { + path: 'about', + component: AboutComponent, + data: { + i18nTitle: 'APP.BROWSE.ABOUT.TITLE' + } + }, { path: '**', component: GenericErrorComponent diff --git a/src/app/components/about/about.component.html b/src/app/components/about/about.component.html new file mode 100644 index 000000000..0142c6069 --- /dev/null +++ b/src/app/components/about/about.component.html @@ -0,0 +1,46 @@ +
+
+
+
Alfresco Content Services
+

version: {{ ecmVersion.edition }} {{ ecmVersion.version.display }}

+
+ +
+
License
+ +
+ +
+
Status
+ +
+ +
+
Modules
+ +
+ +
+
Alfresco Content Application
+

version: 1.0

+
+ + + +
+
Packages
+ + Current project is using the following ADF libraries: + + +
+
+
diff --git a/src/app/components/about/about.component.scss b/src/app/components/about/about.component.scss new file mode 100644 index 000000000..6e5d684cd --- /dev/null +++ b/src/app/components/about/about.component.scss @@ -0,0 +1,37 @@ +@import 'variables'; + +article { + color: $alfresco-secondary-text-color; +} + +article:first-of-type { + padding-bottom: 0; +} + +article:last-of-type { + margin-bottom: 50px; +} + +header { + line-height: 24px; + font-size: 14px; + font-weight: 800; + letter-spacing: -0.2px; +} + +a { + text-decoration: none; + color: $alfresco-primary-text-color; +} + +.padding { + padding: 25px; +} + +.padding-top-bottom { + padding: 25px 0 25px 0; +} + +.padding-left-right { + padding: 0 25px 0 25px; +} diff --git a/src/app/components/about/about.component.ts b/src/app/components/about/about.component.ts new file mode 100644 index 000000000..31fcaddbc --- /dev/null +++ b/src/app/components/about/about.component.ts @@ -0,0 +1,90 @@ +/*! + * @license + * Copyright 2017 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, OnInit } from '@angular/core'; +import { Http } from '@angular/http'; +import { DiscoveryApiService, EcmProductVersionModel } from 'ng2-alfresco-core'; +import { ObjectDataTableAdapter } from 'ng2-alfresco-datatable'; + +@Component({ + selector: 'app-about', + templateUrl: './about.component.html', + styleUrls: [ './about.component.scss' ] +}) +export class AboutComponent implements OnInit { + ecmVersion: EcmProductVersionModel = null; + data: ObjectDataTableAdapter; + status: ObjectDataTableAdapter; + license: ObjectDataTableAdapter; + modules: ObjectDataTableAdapter; + githubUrlCommitAlpha = 'https://github.com/Alfresco/alfresco-content-app/commits'; + + + constructor( + private discovery: DiscoveryApiService, + private http: Http + ) {} + + ngOnInit() { + this.discovery.getEcmProductInfo().subscribe((ecmVers) => { + this.ecmVersion = ecmVers; + + this.modules = new ObjectDataTableAdapter(this.ecmVersion.modules, [ + {type: 'text', key: 'id', title: 'ID', sortable: true}, + {type: 'text', key: 'title', title: 'Title', sortable: true}, + {type: 'text', key: 'version', title: 'Description', sortable: true}, + {type: 'text', key: 'installDate', title: 'Install Date', sortable: true}, + {type: 'text', key: 'installState', title: 'Install State', sortable: true}, + {type: 'text', key: 'versionMin', title: 'Version Minor', sortable: true}, + {type: 'text', key: 'versionMax', title: 'Version Max', sortable: true} + ]); + + this.status = new ObjectDataTableAdapter([this.ecmVersion.status], [ + {type: 'text', key: 'isReadOnly', title: 'Read Only', sortable: true}, + {type: 'text', key: 'isAuditEnabled', title: 'Audit Enable', sortable: true}, + {type: 'text', key: 'isQuickShareEnabled', title: 'Quick Shared Enable', sortable: true}, + {type: 'text', key: 'isThumbnailGenerationEnabled', title: 'Thumbnail Generation', sortable: true} + ]); + + this.license = new ObjectDataTableAdapter([this.ecmVersion.license], [ + {type: 'text', key: 'issuedAt', title: 'Issued At', sortable: true}, + {type: 'text', key: 'expiresAt', title: 'Expires At', sortable: true}, + {type: 'text', key: 'remainingDays', title: 'Remaining Days', sortable: true}, + {type: 'text', key: 'holder', title: 'Holder', sortable: true}, + {type: 'text', key: 'mode', title: 'Type', sortable: true}, + {type: 'text', key: 'isClusterEnabled', title: 'Cluster Enabled', sortable: true}, + {type: 'text', key: 'isCryptodocEnabled', title: 'Cryptodoc Enable', sortable: true} + ]); + }); + + this.http.get('/versions.json').subscribe(response => { + const regexp = new RegExp('^(ng2-alfresco|alfresco-)'); + + const alfrescoPackagesTableRepresentation = Object.keys(response.json().dependencies) + .filter((val) => regexp.test(val)) + .map((val) => ({ + name: val, + version: response.json().dependencies[val].version + })); + + this.data = new ObjectDataTableAdapter(alfrescoPackagesTableRepresentation, [ + {type: 'text', key: 'name', title: 'Name', sortable: true}, + {type: 'text', key: 'version', title: 'Version', sortable: true} + ]); + }); + } +} diff --git a/src/app/components/favorites/favorites.component.html b/src/app/components/favorites/favorites.component.html index 1ec5194c5..beebb6856 100644 --- a/src/app/components/favorites/favorites.component.html +++ b/src/app/components/favorites/favorites.component.html @@ -117,9 +117,10 @@ + title="APP.DOCUMENT_LIST.COLUMNS.LOCATION"> + + + { RouterTestingModule ], declarations: [ + LocationLinkComponent, FavoritesComponent ] }) diff --git a/src/app/components/header/header.component.html b/src/app/components/header/header.component.html index b7718e642..d6d641262 100644 --- a/src/app/components/header/header.component.html +++ b/src/app/components/header/header.component.html @@ -1,9 +1,10 @@ - {{ appName }} + [routerLink]="[ '/' ]"> + {{ appName }} + diff --git a/src/app/components/header/header.component.scss b/src/app/components/header/header.component.scss index 547f328c7..735e23a0c 100644 --- a/src/app/components/header/header.component.scss +++ b/src/app/components/header/header.component.scss @@ -31,21 +31,14 @@ .app-menu__title { width: 100px; height: $app-menu-height; + margin-left: 5px; + display: flex; + justify-content: center; + align-items: stretch; - line-height: 54px; - text-decoration: none; - text-indent: -9999px; - - color: inherit; - - background: url('/assets/images/alfresco-logo-white.svg') no-repeat 0 50%; - background-size: 100% auto; - - display: block; - position: relative; - font-size: 20px; - line-height: 1; - letter-spacing: .02em; - font-weight: 400; + &> img { + width: 100%; + object-fit: contain; + } } } diff --git a/src/app/components/header/header.component.ts b/src/app/components/header/header.component.ts index cc800f46d..1605eaa2a 100644 --- a/src/app/components/header/header.component.ts +++ b/src/app/components/header/header.component.ts @@ -25,10 +25,16 @@ import { AppConfigService } from 'ng2-alfresco-core'; encapsulation: ViewEncapsulation.None }) export class HeaderComponent { + static ASSETS_PATH = '/assets/images/'; + static DEFAULT_LOGO = 'alfresco-logo-white.svg'; constructor(private appConfig: AppConfigService) {} get appName(): string { return this.appConfig.get('application.name'); } + + get logo() { + return `${HeaderComponent.ASSETS_PATH}${this.appConfig.get('logo', HeaderComponent.DEFAULT_LOGO)}`; + } } diff --git a/src/app/components/libraries/libraries.component.html b/src/app/components/libraries/libraries.component.html index baf63e62a..0de1d3bb0 100644 --- a/src/app/components/libraries/libraries.component.html +++ b/src/app/components/libraries/libraries.component.html @@ -23,8 +23,8 @@
group_work -

{{ 'APP.BROWSE.LIBRARIES.EMPTY_STATE.TITLE' | translate }}

-

{{ 'APP.BROWSE.LIBRARIES.EMPTY_STATE.TEXT' | translate }}

+

{{ 'APP.BROWSE.LIBRARIES.EMPTY_STATE.TITLE' | translate }}

+

{{ 'APP.BROWSE.LIBRARIES.EMPTY_STATE.TEXT' | translate }}

diff --git a/src/app/components/location-link/location-link.component.ts b/src/app/components/location-link/location-link.component.ts new file mode 100644 index 000000000..dfbacc866 --- /dev/null +++ b/src/app/components/location-link/location-link.component.ts @@ -0,0 +1,146 @@ +import { Component, Input, ChangeDetectionStrategy, OnInit, ViewEncapsulation } from '@angular/core'; +import { DataColumn, DataRow, DataTableAdapter } from 'ng2-alfresco-datatable'; +import { AlfrescoApiService } from 'ng2-alfresco-core'; +import { PathInfoEntity, AlfrescoApi } from 'alfresco-js-api'; +import { Observable } from 'rxjs/Rx'; + +@Component({ + selector: 'app-location-link', + template: ` + + {{ displayText | async }} + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + // tslint:disable-next-line:use-host-property-decorator + host: { + 'class': 'app-location-link' + } +}) +export class LocationLinkComponent implements OnInit { + + @Input() + context: any; + + @Input() + link: any[]; + + @Input() + displayText: Observable; + + @Input() + tooltip: Observable; + + constructor(private apiService: AlfrescoApiService) { + } + + ngOnInit() { + if (this.context) { + const data: DataTableAdapter = this.context.data; + const col: DataColumn = this.context.col; + const row: DataRow = this.context.row; + const value: PathInfoEntity = data.getValue(row, col); + + if (value.name && value.elements) { + const isLibraryPath = this.isLibraryContent(value); + + this.displayText = this.getDisplayText(value); + this.tooltip = this.getTooltip(value); + + const parent = value.elements[value.elements.length - 1]; + const area = isLibraryPath ? '/libraries' : '/personal-files'; + + this.link = [ area, parent.id ]; + } + } + } + + private isLibraryContent(path: PathInfoEntity): boolean { + if (path && path.elements.length >= 2 && path.elements[1].name === 'Sites') { + return true; + } + return false; + } + + // todo: review once 5.2.3 is out + private getDisplayText(path: PathInfoEntity): Observable { + const elements = path.elements.map(e => e.name); + + // for admin users + if (elements.length === 1 && elements[0] === 'Company Home') { + return Observable.of('Personal Files'); + } + + // for non-admin users + if (elements.length === 3 && elements[0] === 'Company Home' && elements[1] === 'User Homes') { + return Observable.of('Personal Files'); + } + + const result = elements[elements.length - 1]; + + if (result === 'documentLibrary') { + const fragment = path.elements[path.elements.length - 2]; + + return new Observable(observer => { + this.apiService.nodesApi.getNodeInfo(fragment.id).then( + (node) => { + observer.next(node.properties['cm:title'] || node.name || fragment.name); + observer.complete(); + }, + (err) => { + observer.next(fragment.name); + observer.complete(); + } + ); + }); + } + + return Observable.of(result); + } + + // todo: review once 5.2.3 is out + private getTooltip(path: PathInfoEntity): Observable { + const elements = path.elements.map(e => Object.assign({}, e)); + + if (elements[0].name === 'Company Home') { + elements[0].name = 'Personal Files'; + + if (elements.length > 1) { + if (elements[1].name === 'Sites') { + const fragment = elements[2]; + + return new Observable(observer => { + this.apiService.nodesApi.getNodeInfo(fragment.id).then( + (node) => { + elements.splice(0, 2); + elements[0].name = node.properties['cm:title'] || node.name || fragment.name; + elements.splice(1, 1); + elements.unshift({ id: null, name: 'File Libraries' }); + + observer.next(elements.map(e => e.name).join('/')); + observer.complete(); + }, + (err) => { + elements.splice(0, 2); + elements.unshift({ id: null, name: 'File Libraries' }); + elements.splice(2, 1); + + observer.next(elements.map(e => e.name).join('/')); + observer.complete(); + } + ); + }); + } + + if (elements[1].name === 'User Homes') { + elements.splice(0, 3); + elements.unshift({ id: null, name: 'Personal Files'}); + } + } + } + + const result = elements.map(e => e.name).join('/'); + return Observable.of(result); + } +} diff --git a/src/app/components/recent-files/recent-files.component.html b/src/app/components/recent-files/recent-files.component.html index cfa07600b..4607b41c5 100644 --- a/src/app/components/recent-files/recent-files.component.html +++ b/src/app/components/recent-files/recent-files.component.html @@ -84,8 +84,8 @@
access_time -

{{ 'APP.BROWSE.RECENT.EMPTY_STATE.TITLE' | translate }}

-

{{ 'APP.BROWSE.RECENT.EMPTY_STATE.TEXT' | translate }}

+

{{ 'APP.BROWSE.RECENT.EMPTY_STATE.TITLE' | translate }}

+

{{ 'APP.BROWSE.RECENT.EMPTY_STATE.TEXT' | translate }}

@@ -109,9 +109,10 @@ + title="APP.DOCUMENT_LIST.COLUMNS.LOCATION"> + + + { @@ -54,6 +55,7 @@ describe('RecentFiles Routed Component', () => { CommonModule ], declarations: [ + LocationLinkComponent, RecentFilesComponent ] }) diff --git a/src/app/components/shared-files/shared-files.component.html b/src/app/components/shared-files/shared-files.component.html index 77ad86999..142e62856 100644 --- a/src/app/components/shared-files/shared-files.component.html +++ b/src/app/components/shared-files/shared-files.component.html @@ -107,11 +107,13 @@ + title="APP.DOCUMENT_LIST.COLUMNS.LOCATION"> + + + + { @@ -51,6 +52,7 @@ describe('SharedFilesComponent', () => { CommonModule ], declarations: [ + LocationLinkComponent, SharedFilesComponent ] }) diff --git a/src/app/components/sidenav/sidenav.component.html b/src/app/components/sidenav/sidenav.component.html index 73973dbfd..97fba621b 100644 --- a/src/app/components/sidenav/sidenav.component.html +++ b/src/app/components/sidenav/sidenav.component.html @@ -56,7 +56,7 @@ {{ item.icon }} diff --git a/src/app/components/sidenav/sidenav.component.ts b/src/app/components/sidenav/sidenav.component.ts index 9c74d08f7..c7e8a8762 100644 --- a/src/app/components/sidenav/sidenav.component.ts +++ b/src/app/components/sidenav/sidenav.component.ts @@ -20,7 +20,7 @@ import { Subscription } from 'rxjs/Rx'; import { Component, OnInit, OnDestroy } from '@angular/core'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; -import { AlfrescoContentService } from 'ng2-alfresco-core'; +import { AlfrescoContentService, AppConfigService } from 'ng2-alfresco-core'; import { BrowsingFilesService } from '../../common/services/browsing-files.service'; @@ -32,60 +32,18 @@ import { BrowsingFilesService } from '../../common/services/browsing-files.servi export class SidenavComponent implements OnInit, OnDestroy { node: MinimalNodeEntryEntity = null; onChangeParentSubscription: Subscription; - - navigation = [ - [ - { - icon: 'folder', - label: 'APP.BROWSE.PERSONAL.SIDENAV_LINK.LABEL', - title: 'APP.BROWSE.PERSONAL.SIDENAV_LINK.TOOLTIP', - disabled: false, - route: { url: '/personal-files' } - }, - { - icon: 'group_work', - label: 'APP.BROWSE.LIBRARIES.SIDENAV_LINK.LABEL', - title: 'APP.BROWSE.LIBRARIES.SIDENAV_LINK.TOOLTIP', - disabled: false, - route: { url: '/libraries' } - } - ], - [ - { - icon: 'people', - label: 'APP.BROWSE.SHARED.SIDENAV_LINK.LABEL', - title: 'APP.BROWSE.SHARED.SIDENAV_LINK.TOOLTIP', - disabled: false, - route: { url: '/shared' } - }, - { - icon: 'schedule', - label: 'APP.BROWSE.RECENT.SIDENAV_LINK.LABEL', - title: 'APP.BROWSE.RECENT.SIDENAV_LINK.TOOLTIP', - disabled: false, - route: { url: '/recent-files' } - }, - { - icon: 'star', - label: 'APP.BROWSE.FAVORITES.SIDENAV_LINK.LABEL', - title: 'APP.BROWSE.FAVORITES.SIDENAV_LINK.TOOLTIP', - disabled: false, - route: { url: '/favorites' } - }, - { - icon: 'delete', - label: 'APP.BROWSE.TRASHCAN.SIDENAV_LINK.LABEL', - title: 'APP.BROWSE.TRASHCAN.SIDENAV_LINK.TOOLTIP', - disabled: false, - route: { url: '/trashcan' } - } - ] - ]; + navigation = []; constructor( private browsingFilesService: BrowsingFilesService, - private contentService: AlfrescoContentService - ) {} + private contentService: AlfrescoContentService, + private appConfig: AppConfigService + ) { + this.navigation = this.navigation.concat([ + this.appConfig.get('navigation.main'), + this.appConfig.get('navigation.secondary') + ]); + } ngOnInit() { this.onChangeParentSubscription = this.browsingFilesService.onChangeParent diff --git a/src/app/components/trashcan/trashcan.component.html b/src/app/components/trashcan/trashcan.component.html index 107d58efe..9675b9e96 100644 --- a/src/app/components/trashcan/trashcan.component.html +++ b/src/app/components/trashcan/trashcan.component.html @@ -66,27 +66,24 @@ - - {{ (value || '').split('/').pop() }} + + - - {{ value | adfFileSize }} - + title="APP.DOCUMENT_LIST.COLUMNS.SIZE" + type="fileSize"> - - {{ value | adfTimeAgo }} - + title="APP.DOCUMENT_LIST.COLUMNS.DELETED_ON" + type="date" + format="timeAgo"> { let fixture; @@ -42,6 +43,7 @@ describe('TrashcanComponent', () => { CommonModule ], declarations: [ + LocationLinkComponent, TrashcanComponent ] }) diff --git a/src/app/ui/_layout.scss b/src/app/ui/_layout.scss index 59b0026d9..8bcb6cd30 100644 --- a/src/app/ui/_layout.scss +++ b/src/app/ui/_layout.scss @@ -44,6 +44,10 @@ $app-inner-layout--footer-height: 48px; flex: 1; flex-direction: column; + &--scroll { + overflow: auto; + } + &__header, &__footer { display: flex; @@ -65,6 +69,10 @@ $app-inner-layout--footer-height: 48px; overflow: hidden; } + &__content--scroll { + overflow: auto; + } + &__footer { flex-basis: $app-inner-layout--footer-height; border-top: 1px solid $alfresco-divider-color; diff --git a/src/app/ui/overrides/_alfresco-document-list.scss b/src/app/ui/overrides/_alfresco-document-list.scss index ff5c57c41..ea0cd2627 100644 --- a/src/app/ui/overrides/_alfresco-document-list.scss +++ b/src/app/ui/overrides/_alfresco-document-list.scss @@ -14,14 +14,18 @@ adf-document-list { } } -adf-document-list .adf-data-table { +.adf-data-table { border: none !important; .sr-only { display: none; } - tr, td { + th, td { + color: $alfresco-secondary-text-color; + } + + th, tr, td { &:focus { outline: none !important; } @@ -76,10 +80,25 @@ adf-document-list .adf-data-table { } } + .adf-data-table__header--sorted-asc, + .adf-data-table__header--sorted-desc { + &:hover { + &:before { + color: inherit !important; + } + } + } + + .app-location-link, .adf-location-cell { a { text-decoration: none; color: $alfresco-primary-text-color; + + &:hover { + color: $alfresco-app-color--default; + text-decoration: underline; + } } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 5c6ec695c..78d406d63 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -3,6 +3,9 @@ "LANGUAGE": "Language", "SIGN_IN": "Sign in", "SIGN_OUT": "Sign out", + "PREVIEW": { + "TITLE": "Preview" + }, "NEW_MENU": { "LABEL": "New", "MENU_ITEMS": { @@ -82,6 +85,9 @@ "FIRST_TEXT": "Items you delete are moved to the Trash.", "SECOND_TEXT": "Empty Trash to permanently delete items." } + }, + "ABOUT": { + "TITLE": "About" } }, "ACTIONS": { diff --git a/wsrv-config.js b/wsrv-config.js deleted file mode 100644 index 45a802a66..000000000 --- a/wsrv-config.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - "host": "0.0.0.0", - "port": 3000, - "dir": "./dist", - "spa": true, - "proxy": { - "/alfresco/{p*}": { - "options": { - "uri": "http://0.0.0.0:8080/alfresco/{p}" - } - } - }, - onResHeaders(headers) { - if (headers) { - const authHeader = headers['www-authenticate']; - if (authHeader) { - headers['www-authenticate'] = `x${authHeader}`; - } - } - } -} diff --git a/yarn.lock b/yarn.lock index 7164cf2fb..fed4f32ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -203,11 +203,11 @@ version "2.6.2" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.6.2.tgz#6e6d4cb183cd55c7a1ad6270bced10fdd5367a3c" -"@types/jasmine@~2.5.53": - version "2.5.54" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.54.tgz#a6b5f2ae2afb6e0307774e8c7c608e037d491c63" +"@types/jasmine@^2.5.53": + version "2.8.2" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.2.tgz#6ae4d8740c0da5d5a627df725b4eed71b8e36668" -"@types/jasminewd2@~2.0.2": +"@types/jasminewd2@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.3.tgz#0d2886b0cbdae4c0eeba55e30792f584bf040a95" dependencies: @@ -229,13 +229,6 @@ abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" -accept@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/accept/-/accept-2.1.4.tgz#887af54ceee5c7f4430461971ec400c61d09acbb" - dependencies: - boom "5.x.x" - hoek "4.x.x" - accepts@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" @@ -326,13 +319,6 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -ammo@2.x.x, ammo@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/ammo/-/ammo-2.0.4.tgz#bf80aab211698ea78f63ef5e7f113dd5d9e8917f" - dependencies: - boom "5.x.x" - hoek "4.x.x" - ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -516,10 +502,6 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -b64@3.x.x: - version "3.0.3" - resolved "https://registry.yarnpkg.com/b64/-/b64-3.0.3.tgz#36afeee0d9345f046387ce6de8a6702afe5bb56e" - babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -678,15 +660,6 @@ body-parser@1.18.2, body-parser@^1.16.1: raw-body "2.3.2" type-is "~1.6.15" -body@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" - dependencies: - continuable-cache "^0.3.1" - error "^7.0.0" - raw-body "~1.1.0" - safe-json-parse "~1.0.1" - bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" @@ -714,7 +687,7 @@ boom@4.x.x: dependencies: hoek "4.x.x" -boom@5.x.x, boom@^5.2.0: +boom@5.x.x: version "5.2.0" resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" dependencies: @@ -828,21 +801,10 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" -bytes@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" -call@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/call/-/call-4.0.2.tgz#df76f5f51ee8dd48b856ac8400f7e69e6d7399c4" - dependencies: - boom "5.x.x" - hoek "4.x.x" - callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" @@ -894,20 +856,6 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" -catbox-memory@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/catbox-memory/-/catbox-memory-2.0.4.tgz#433e255902caf54233d1286429c8f4df14e822d5" - dependencies: - hoek "4.x.x" - -catbox@^7.1.5: - version "7.1.5" - resolved "https://registry.yarnpkg.com/catbox/-/catbox-7.1.5.tgz#c56f7e8e9555d27c0dc038a96ef73e57d186bb1f" - dependencies: - boom "5.x.x" - hoek "4.x.x" - joi "10.x.x" - center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -937,7 +885,7 @@ charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" -chokidar@1.7.0, chokidar@^1.4.1, chokidar@^1.6.0, chokidar@^1.7.0: +chokidar@^1.4.1, chokidar@^1.6.0, chokidar@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: @@ -1164,16 +1112,6 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -content@3.x.x: - version "3.0.6" - resolved "https://registry.yarnpkg.com/content/-/content-3.0.6.tgz#9c2e301e9ae515ed65a4b877d78aa5659bb1b809" - dependencies: - boom "5.x.x" - -continuable-cache@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" - convert-source-map@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" @@ -1211,6 +1149,10 @@ core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1: version "2.5.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" +core-js@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" + core-object@^3.1.0: version "3.1.5" resolved "https://registry.yarnpkg.com/core-object/-/core-object-3.1.5.tgz#fa627b87502adc98045e44678e9a8ec3b9c0d2a9" @@ -1285,7 +1227,7 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" -cryptiles@3.x.x, cryptiles@^3.1.2: +cryptiles@3.x.x: version "3.1.2" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" dependencies: @@ -1447,7 +1389,7 @@ debug@*, debug@^3.1.0: dependencies: ms "2.0.0" -debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9, debug@~2.6.7: +debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1645,15 +1587,6 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" -duplexify@^3.1.2: - version "3.5.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd" - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -1696,12 +1629,6 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" - dependencies: - once "^1.4.0" - engine.io-client@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" @@ -1770,13 +1697,6 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -error@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" - dependencies: - string-template "~0.2.1" - xtend "~4.0.0" - es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.7: version "0.10.35" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.35.tgz#18ee858ce6a3c45c7d79e91c15fcca9ec568494f" @@ -1803,6 +1723,10 @@ es6-map@^0.1.3: es6-symbol "~3.1.1" event-emitter "~0.3.5" +es6-promise@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" + es6-set@~0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" @@ -2024,15 +1948,11 @@ fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" -fast-safe-stringify@1.1.x: - version "1.1.13" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-1.1.13.tgz#a01e9cd9c9e491715c98a75a42d5f0bbd107ff76" - fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" -faye-websocket@^0.10.0, faye-websocket@~0.10.0: +faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" dependencies: @@ -2340,44 +2260,10 @@ globule@^1.0.0: lodash "~4.17.4" minimatch "~3.0.2" -good-console@6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/good-console/-/good-console-6.4.0.tgz#7294c9d90c4c9f059a082e180625495966d2ba59" - dependencies: - hoek "4.x.x" - joi "8.1.x" - json-stringify-safe "5.0.x" - moment "2.15.x" - -good-squeeze@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/good-squeeze/-/good-squeeze-5.0.2.tgz#a8e58242b4a0b32cdbdf317b60e73a19a7f0879b" - dependencies: - fast-safe-stringify "1.1.x" - hoek "4.x.x" - -good@7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/good/-/good-7.3.0.tgz#25da74d51f336692ec86fe8d6533453585fa85fe" - dependencies: - hoek "4.x.x" - joi "10.x.x" - oppsy "1.x.x" - pumpify "1.3.x" - graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -h2o2@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/h2o2/-/h2o2-6.0.1.tgz#7510c93506327099d35b289b7bca1af92b9fb683" - dependencies: - boom "5.x.x" - hoek "4.x.x" - joi "10.x.x" - wreck "12.x.x" - hammerjs@2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" @@ -2404,29 +2290,6 @@ handlebars@^4.0.3: optionalDependencies: uglify-js "^2.6" -hapi@16.6.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/hapi/-/hapi-16.6.0.tgz#c97cc7119a04314553883868651862fee34318ee" - dependencies: - accept "^2.1.4" - ammo "^2.0.4" - boom "^5.2.0" - call "^4.0.2" - catbox "^7.1.5" - catbox-memory "^2.0.4" - cryptiles "^3.1.2" - heavy "^4.0.4" - hoek "^4.2.0" - iron "^4.0.5" - items "^2.1.1" - joi "^10.6.0" - mimos "^3.0.3" - podium "^1.3.0" - shot "^3.4.2" - statehood "^5.0.3" - subtext "^5.0.0" - topo "^2.0.2" - har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" @@ -2525,14 +2388,6 @@ he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" -heavy@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/heavy/-/heavy-4.0.4.tgz#36c91336c00ccfe852caa4d153086335cd2f00e9" - dependencies: - boom "5.x.x" - hoek "4.x.x" - joi "10.x.x" - hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -2545,7 +2400,7 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoek@4.x.x, hoek@^4.2.0: +hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" @@ -2698,6 +2553,10 @@ img-stats@^0.5.2: dependencies: xmldom "^0.1.19" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + in-publish@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" @@ -2716,17 +2575,6 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" -inert@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/inert/-/inert-4.2.1.tgz#da743c478a18a8378032f80ada128a28cd2bba93" - dependencies: - ammo "2.x.x" - boom "5.x.x" - hoek "4.x.x" - items "2.x.x" - joi "10.x.x" - lru-cache "4.1.x" - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2774,14 +2622,6 @@ ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" -iron@4.x.x, iron@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" - dependencies: - boom "5.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -2950,10 +2790,6 @@ isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" -isemail@2.x.x: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isemail/-/isemail-2.2.1.tgz#0353d3d9a62951080c262c2aa0a42b8ea8e9e2a6" - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -3044,32 +2880,24 @@ istanbul-reports@^1.1.3: dependencies: handlebars "^4.0.3" -items@2.x.x, items@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198" - -jasmine-core@~2.6.2: - version "2.6.4" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.4.tgz#dec926cd0a9fa287fb6db5c755fa487e74cecac5" - -jasmine-core@~2.8.0: +jasmine-core@^2.6.2, jasmine-core@~2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" -jasmine-reporters@2.2.1: +jasmine-reporters@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jasmine-reporters/-/jasmine-reporters-2.2.1.tgz#de9a9201367846269e7ca8adff5b44221671fcbd" dependencies: mkdirp "^0.5.1" xmldom "^0.1.22" -jasmine-spec-reporter@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.1.1.tgz#5a6d58ab5d61bea7309fbc279239511756b1b588" +jasmine-spec-reporter@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz#1d632aec0341670ad324f92ba84b4b32b35e9e22" dependencies: colors "1.1.2" -jasmine2-protractor-utils@1.3.0: +jasmine2-protractor-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jasmine2-protractor-utils/-/jasmine2-protractor-utils-1.3.0.tgz#94046aabbc74ad0a4b746bcd4dc30507587b67e3" dependencies: @@ -3089,24 +2917,6 @@ jasminewd2@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e" -joi@10.x.x, joi@^10.6.0: - version "10.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-10.6.0.tgz#52587f02d52b8b75cdb0c74f0b164a191a0e1fc2" - dependencies: - hoek "4.x.x" - isemail "2.x.x" - items "2.x.x" - topo "2.x.x" - -joi@8.1.x: - version "8.1.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-8.1.1.tgz#2d8b52a5d909d217ed47248577eefe8b1798f48f" - dependencies: - hoek "4.x.x" - isemail "2.x.x" - moment "2.x.x" - topo "2.x.x" - js-base64@^2.1.5, js-base64@^2.1.8, js-base64@^2.1.9: version "2.3.2" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.3.2.tgz#a79a923666372b580f8e27f51845c6f7e8fbfbaf" @@ -3159,7 +2969,7 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -3196,6 +3006,16 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jszip@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.5.tgz#e3c2a6c6d706ac6e603314036d43cd40beefdf37" + dependencies: + core-js "~2.3.0" + es6-promise "~3.0.2" + lie "~3.1.0" + pako "~1.0.2" + readable-stream "~2.0.6" + karma-chrome-launcher@~2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf" @@ -3329,9 +3149,11 @@ license-webpack-plugin@^1.0.0: dependencies: ejs "^2.5.7" -livereload-js@2.2.2, livereload-js@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.2.2.tgz#6c87257e648ab475bc24ea257457edcc1f8d0bc2" +lie@~3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + dependencies: + immediate "~3.0.5" load-json-file@^1.0.0: version "1.1.0" @@ -3408,14 +3230,14 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@4.17.4, lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.4: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - lodash@^3.8.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" +lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + log4js@^0.6.31: version "0.6.38" resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" @@ -3452,7 +3274,7 @@ lru-cache@2.2.x: version "2.2.4" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" -lru-cache@4.1.x, lru-cache@^4.0.1: +lru-cache@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" dependencies: @@ -3561,7 +3383,7 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.x.x, "mime-db@>= 1.30.0 < 2": +"mime-db@>= 1.30.0 < 2": version "1.31.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.31.0.tgz#a49cd8f3ebf3ed1a482b60561d9105ad40ca74cb" @@ -3583,13 +3405,6 @@ mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" -mimos@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/mimos/-/mimos-3.0.3.tgz#b9109072ad378c2b72f6a0101c43ddfb2b36641f" - dependencies: - hoek "4.x.x" - mime-db "1.x.x" - minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" @@ -3629,14 +3444,10 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd dependencies: minimist "0.0.8" -moment@2.15.2, moment@2.15.x: +moment@2.15.2: version "2.15.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.2.tgz#1bfdedf6a6e345f322fe956d5df5bd08a8ce84dc" -moment@2.x.x: - version "2.19.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" - ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -3856,13 +3667,6 @@ ng2-alfresco-viewer@1.10.0-beta6: systemjs "0.19.27" zone.js "0.8.12" -nigel@2.x.x: - version "2.0.2" - resolved "https://registry.yarnpkg.com/nigel/-/nigel-2.0.2.tgz#93a1866fb0c52d87390aa75e2b161f4b5c75e5b1" - dependencies: - hoek "4.x.x" - vise "2.x.x" - no-case@^2.2.0: version "2.3.2" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" @@ -4091,7 +3895,7 @@ on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" -once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: +once@^1.3.0, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -4104,19 +3908,12 @@ opn@4.0.2: object-assign "^4.0.1" pinkie-promise "^2.0.0" -opn@5.1.0, opn@~5.1.0: +opn@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519" dependencies: is-wsl "^1.1.0" -oppsy@1.x.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/oppsy/-/oppsy-1.0.2.tgz#98014cd6967653a83cfffa554226dc90050baad4" - dependencies: - hoek "4.x.x" - items "2.x.x" - optimist@^0.6.1, optimist@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -4195,6 +3992,10 @@ pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" +pako@~1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + param-case@2.1.x: version "2.1.1" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" @@ -4331,16 +4132,6 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" -pez@2.x.x: - version "2.1.5" - resolved "https://registry.yarnpkg.com/pez/-/pez-2.1.5.tgz#5ec2cc62500cc3eb4236d4a414cf5a17b5eb5007" - dependencies: - b64 "3.x.x" - boom "5.x.x" - content "3.x.x" - hoek "4.x.x" - nigel "2.x.x" - pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4359,14 +4150,6 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" -podium@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/podium/-/podium-1.3.0.tgz#3c490f54d16f10f5260cbe98641f1cb733a8851c" - dependencies: - hoek "4.x.x" - items "2.x.x" - joi "10.x.x" - portfinder@^1.0.9, portfinder@~1.0.12: version "1.0.13" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" @@ -4694,9 +4477,9 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -protractor@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.1.2.tgz#9b221741709a4c62d5cd53c6aadd54a71137e95f" +protractor@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.2.0.tgz#d3f39b195e85f3539ad9d8cb6560a9d2b63297c4" dependencies: "@types/node" "^6.0.46" "@types/q" "^0.0.32" @@ -4709,7 +4492,7 @@ protractor@~5.1.2: optimist "~0.6.0" q "1.4.1" saucelabs "~1.3.0" - selenium-webdriver "3.0.1" + selenium-webdriver "3.6.0" source-map-support "~0.4.0" webdriver-js-extender "^1.0.0" webdriver-manager "^12.0.6" @@ -4739,21 +4522,6 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -pump@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@1.3.x: - version "1.3.5" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.5.tgz#1b671c619940abcaeac0ad0e3a3c164be760993b" - dependencies: - duplexify "^3.1.2" - inherits "^2.0.1" - pump "^1.0.0" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -4774,7 +4542,7 @@ qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" -qs@6.5.1, qs@^6.4.0, qs@^6.5.1, qs@~6.5.1: +qs@6.5.1, qs@^6.5.1, qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" @@ -4838,13 +4606,6 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" - dependencies: - bytes "1" - string_decoder "0.10" - raw-loader@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" @@ -4897,7 +4658,7 @@ readable-stream@1.0, readable-stream@~1.0.2: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.6, readable-stream@^2.2.9: +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.6, readable-stream@^2.2.9: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -4909,6 +4670,17 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.0.3" util-deprecate "~1.0.1" +readable-stream@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -5093,7 +4865,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: +rimraf@2, rimraf@2.6.2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: @@ -5122,10 +4894,6 @@ safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -safe-json-parse@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" - sass-graph@^2.1.1: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" @@ -5180,11 +4948,11 @@ select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" -selenium-webdriver@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz#a2dea5da4a97f6672e89e7ca7276cefa365147a7" +selenium-webdriver@3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz#2ba87a1662c020b8988c981ae62cb2a01298eafc" dependencies: - adm-zip "^0.4.7" + jszip "^3.1.3" rimraf "^2.5.4" tmp "0.0.30" xml2js "^0.4.17" @@ -5312,13 +5080,6 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -shot@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/shot/-/shot-3.4.2.tgz#1e5c3f6f2b26649adc42f7eb350214a5a0291d67" - dependencies: - hoek "4.x.x" - joi "10.x.x" - signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -5506,17 +5267,6 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -statehood@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/statehood/-/statehood-5.0.3.tgz#c07a75620db5379b60d2edd47f538002a8ac7dd6" - dependencies: - boom "5.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - iron "4.x.x" - items "2.x.x" - joi "10.x.x" - "statuses@>= 1.3.1 < 2": version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" @@ -5548,18 +5298,10 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -5575,7 +5317,7 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string_decoder@0.10, string_decoder@^0.10.25, string_decoder@~0.10.x: +string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -5650,16 +5392,6 @@ stylus@^0.54.5: sax "0.5.x" source-map "0.1.x" -subtext@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/subtext/-/subtext-5.0.0.tgz#9c3f083018bb1586b167ad8cfd87083f5ccdfe0f" - dependencies: - boom "5.x.x" - content "3.x.x" - hoek "4.x.x" - pez "2.x.x" - wreck "12.x.x" - superagent@3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.7.0.tgz#bd58bfde2cbc5305adb9ccbb6dacba18408629d6" @@ -5756,17 +5488,6 @@ timers-browserify@^2.0.2: dependencies: setimmediate "^1.0.4" -tiny-lr@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.0.5.tgz#21f40bf84ebd1f853056680375eef1670c334112" - dependencies: - body "^5.1.0" - debug "~2.6.7" - faye-websocket "~0.10.0" - livereload-js "^2.2.2" - object-assign "^4.1.0" - qs "^6.4.0" - tmp@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" @@ -5801,12 +5522,6 @@ to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" -topo@2.x.x, topo@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" - dependencies: - hoek "4.x.x" - toposort@^1.0.0: version "1.0.6" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.6.tgz#c31748e55d210effc00fdcdc7d6e68d7d7bb9cec" @@ -6079,12 +5794,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vise@2.x.x: - version "2.0.2" - resolved "https://registry.yarnpkg.com/vise/-/vise-2.0.2.tgz#6b08e8fb4cb76e3a50cd6dd0ec37338e811a0d39" - dependencies: - hoek "4.x.x" - vlq@^0.2.1: version "0.2.3" resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" @@ -6294,13 +6003,6 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -wreck@12.x.x: - version "12.5.1" - resolved "https://registry.yarnpkg.com/wreck/-/wreck-12.5.1.tgz#cd2ffce167449e1f0242ed9cf80552e20fb6902a" - dependencies: - boom "5.x.x" - hoek "4.x.x" - ws@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" @@ -6315,23 +6017,6 @@ ws@^1.0.1: options ">=0.0.5" ultron "1.0.x" -wsrv@0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/wsrv/-/wsrv-0.2.2.tgz#16b9623c3802dee21c54978ae882aaea9c721ae0" - dependencies: - chokidar "1.7.0" - good "7.3.0" - good-console "6.4.0" - good-squeeze "5.0.2" - h2o2 "6.0.1" - hapi "16.6.0" - inert "4.2.1" - livereload-js "2.2.2" - lodash "4.17.4" - opn "5.1.0" - tiny-lr "1.0.5" - yargs "9.0.1" - wtf-8@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a" @@ -6366,7 +6051,7 @@ xmlhttprequest-ssl@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" -xtend@^4.0.0, xtend@~4.0.0: +xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -6396,24 +6081,6 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" -yargs@9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c" - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - yargs@^6.0.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"