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.
+
+
+
+## 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 @@
+
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 @@