Merge pull request #878 from Alfresco/development

1.6.0
This commit is contained in:
Cilibiu Bogdan
2018-12-12 20:36:40 +02:00
committed by GitHub
336 changed files with 25636 additions and 8685 deletions
-62
View File
@@ -1,62 +0,0 @@
version: 2
jobs:
test:
working_directory: ~/alfresco-content-app
docker:
- image: circleci/node:8-browsers
steps:
- checkout
- restore_cache:
key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }}
- run: npm install
- save_cache:
key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }}
paths:
- "node_modules"
- run: xvfb-run -a npm run test:ci
lint:
working_directory: ~/alfresco-content-app
docker:
- image: circleci/node:8-browsers
steps:
- checkout
- restore_cache:
key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }}
- run: npm install
- run: npm run lint
spellcheck:
working_directory: ~/alfresco-content-app
docker:
- image: circleci/node:8-browsers
steps:
- checkout
- restore_cache:
key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }}
- run: npm install
- run: npm run spellcheck
build:
working_directory: ~/alfresco-content-app
docker:
- image: circleci/node:8-browsers
steps:
- checkout
- restore_cache:
key: alfresco-content-app-{{ .Branch }}-{{ checksum "package.json" }}
- run: npm install
- run: npm run build
workflows:
version: 2
build_and_test:
jobs:
- test
- lint:
requires:
- test
- spellcheck:
requires:
- test
- build:
requires:
- test
-1
View File
@@ -4,7 +4,6 @@
/dist
/tmp
/out-tsc
/src/versions.json
# dependencies
/node_modules
+1 -6
View File
@@ -4,11 +4,6 @@ services:
- docker
addons:
chrome: stable
artifacts:
paths:
- e2e-output
target_paths:
- /${TRAVIS_BUILD_NUMBER}
language: node_js
node_js:
- '8'
@@ -34,7 +29,7 @@ jobs:
- bash <(curl -s https://codecov.io/bash) -X gcov
- stage: e2e
name: 'Nginx'
script: npm run build && npm run e2e:docker
script: npm run build.e2e && npm run e2e:docker
- stage: e2e
name: 'Tomcat'
script: npm run build.tomcat.e2e && npm run docker.tomcat.e2e
-3
View File
@@ -4,9 +4,6 @@
"typescript.preferences.quoteStyle": "single",
"typescript.preferences.importModuleSpecifier": "relative",
"editor.formatOnSave": true,
"[html]": {
"editor.formatOnSave": false
},
"[json]": {
"editor.formatOnSave": false
}
+30 -24
View File
@@ -1,25 +1,24 @@
<p align="left"> <img title="Alfresco" src="alfresco.png" alt="Alfresco - make business flow"></p>
# Example Content Application
# Alfresco Content Application
Testing Supported By<br/>
<img width="160" src="docs/images/Browserstack-logo.svg" alt="BrowserStack"/>
## Introduction
The Alfresco Content Application is an example application built using
The Alfresco Content Application a file management application built using
[Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components and was generated with [Angular CLI](https://github.com/angular/angular-cli).
### Who is this example application for
### Who is this application for
This project demonstrates how to construct an application for Alfresco Content Services using the Alfresco ADF and it represents a meaningful composition of ADF components that provide end users with a simple easy to use interface for working with files in the content repository.
The Content Application is a streamlined experience for end users on top of Alfresco Content Services, focused on file management within the content repository. It provides developers with an easily extendable environment for lightening fast custom application development by providing safe ways to inject custom controls, viewer components, pages and plug-ins without upgrade concerns.
### Where to get help
There are a number of resources available to help get you started with the Content App and the ADF:
* [Content App Documentation](https://alfresco.github.io/alfresco-content-app/)
* [Alfresco ADF Documentation](https://alfresco.github.io/adf-component-catalog/)
* [Alfresco Community](https://community.alfresco.com/)
* [ADF Gitter Channel](https://gitter.im/Alfresco/alfresco-ng2-components)
To get help on Angular CLI use ng help or read the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
@@ -28,42 +27,49 @@ Isuses can be raised in GitHub or in the Alfresco JIRA project.
Please include a clear description, steps to reproduce and screenshots where appropriate.All issues will be reviewed; bugs will be categorized if reproducible and enhancement/feature suggestions will be considered against existing priorities if the use case serves a general-purpose need.
#### Features added in the latest release
* Application Extensibility - Phase 2
* Document list presets
* File viewer actions
* Create menu button
* Application header
* Metadata card configuration
* see [Documentation](https://alfresco.github.io/alfresco-content-app/#/extending) for details.
* Sharing Files
* Set expiry date for shared links
* Right click action to access shared link settings
* Automatic disable of sharing based on respository configuration
* Library Management
* For end users: Join and favorite libraries
* New search input to find Libraries, files and/or folders
* Updated action toolbar, right click context menu and refresh of some icons
Please refer to the [release notes](https://github.com/Alfresco/alfresco-content-app/releases) for details of all changes.
#### High level features planned for H2 2018 (July - December)
* Library Management
* For end users: Find, join and favorite libraries
#### High level feature themes planned for 2019
* Collaboration & File Management
* Edit offline, and edit online with Alfresco Office Services (AOS)
* Folder rule creation
* File/Folder linking via secondary association
* File Library Management
* For managers: Create libraries, manage users and requests to join
* Single Sign On
* Support for Alfresco Identity Service
* Enhanced UI and user experiance
* Search result facet improvements
* Search query input assistance
* Metadata information drawer enhancements
### Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing](https://github.com/Alfresco/alfresco-content-app/blob/master/CONTRIBUTING.md) and then check out one of our issues in the [Jira](https://issues.alfresco.com/jira/projects/ACA) or [GitHub](https://github.com/Alfresco/alfresco-content-app/issues)
## Available Features
| Feature | Description |
|------------------|----------------------------------------------------------------|
| Document List | Folder/File browsing of Personal Files, and File Libraries |
| My Files | Folder/File browsing of Personal Files.|
| File Libraries | Create, find, join and browse the file libraries of sites created in the repository.|
| Shared Files | Lists all files that have shared. |
| Recent Files | List files created and/or modified by the logged users within the last 30 days|
| Recent Files | List files created and/or modified by the logged users within the last 30 days.|
| Favorites | Lists all favorited files for the user. |
| Trash | Lists all deleted items stored in the trash can, users can restore or permanently remove. Admin user will see items deleted by all users.|
| Upload | Files and folders can be uploaded through the New button or by dragging and dropping into the browser.|
| Search | Quick search with live results, and full faceted search results page.|
| Actions | A number of actions can be performed on files and/or folders, either individually or multiples at a time|
| Viewer | Viewing files in natively in the browser, unsupported formats are transformed by the repository |
| Actions | A number of actions can be performed on files and/or folders, either individually or multiples at a time.|
| Viewer | Viewing files in natively in the browser, unsupported formats are transformed by the repository. |
| Metadata | The information drawer can be configured in the app.config.json to display metadata information, by default file the Properties Aspect is shown and images will also include EXIF information.|
| File Sharing | Share files, with time expiry if required, externally with uniquely generated URLs.|
| Versioning | The version manager provides access and management of previous file versions, and the ability to upload new versions.|
| Permissions | Granular user permission management of the folders and files throughout the repository.|
| Extensibility | The application provides safe extension points allowing full customiation; see [Documentation](https://alfresco.github.io/alfresco-content-app/#/extending) for details. |
## Further help
+24 -4
View File
@@ -25,7 +25,6 @@
"src/assets",
"src/favicon-96x96.png",
"src/app.config.json",
"src/versions.json",
{
"glob": "**/*",
"input": "node_modules/@alfresco/adf-core/prebuilt-themes",
@@ -85,6 +84,23 @@
"with": "src/environments/environment.prod.ts"
}
]
},
"e2e": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.e2e.ts"
}
]
}
}
},
@@ -99,6 +115,9 @@
"configurations": {
"production": {
"browserTarget": "app:build:production"
},
"e2e": {
"browserTarget": "app:build:e2e"
}
}
},
@@ -135,7 +154,6 @@
"src/assets",
"src/favicon-96x96.png",
"src/app.config.json",
"src/versions.json",
{
"glob": "**/*",
"input": "node_modules/@alfresco/adf-core/prebuilt-themes",
@@ -167,7 +185,8 @@
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
"**/node_modules/**",
"package.json"
]
}
}
@@ -197,7 +216,8 @@
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
"**/node_modules/**",
"package.json"
]
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
npm run build -- --base-href ./
npm run build.e2e -- --base-href ./
node -e "
const fs = require('fs');
+4 -1
View File
@@ -6,6 +6,7 @@
"sharedlinks",
"Redistributable",
"fullscreen",
"LGPL",
"ngrx",
"ngstack",
@@ -34,6 +35,7 @@
"nginx",
"docx",
"SOLR",
"simpletask",
"unshare",
"qshare",
@@ -54,7 +56,8 @@
"keycodes",
"denysvuika",
"submenu",
"submenus"
"submenus",
"simpletask"
],
"dictionaries": ["html", "en-gb", "en_US"]
}
+2
View File
@@ -5,6 +5,8 @@ server {
proxy_pass_request_headers on;
proxy_pass_header Set-Cookie;
access_log off;
location / {
proxy_pass http://content-app;
+10 -407
View File
@@ -1,416 +1,19 @@
# Alfresco Example Content Application
# Alfresco Content Application
<!-- markdownlint-disable MD033 -->
## Introduction
The Alfresco Content Application a file management application built using
[Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components and was generated with [Angular CLI](https://github.com/angular/angular-cli).
The Alfresco Content Application is an example application built using
[Alfresco Application Development Framework (ADF)](https://github.com/Alfresco/alfresco-ng2-components) components.
## Documentation
### Who is this example application for?
The documentation is divided into the following sections:
This example application demonstrates to Angular software engineers
how to construct a content application using the Alfresco ADF.
This example application represents a meaningful composition of ADF components that provide end users
with a simple and easy to use interface for working with files stored in the Alfresco Content Services repository.
### Prerequisites
This application was created based on the latest releases from Alfresco:
- [Alfresco ADF (2.6.0)](https://community.alfresco.com/community/application-development-framework/pages/get-started)
- [Alfresco Content Services (6.0.0)](https://www.alfresco.com/platform/content-services-ecm)
or [Alfresco Community Edition (6.0 - General Release: 201806)](https://www.alfresco.com/products/community/download)
<p class="warning">
You also need <a href="https://nodejs.org/en/" target="_blank">node.js</a> (LTS) installed to build it locally from source code.
</p>
**Note:** The latest version of the Alfresco Content platform is required due to the application using the latest [REST APIs](https://docs.alfresco.com/5.2/pra/1/topics/pra-welcome.html) developments.
## Features
The concept of this example is a simple user interface which makes accessing files in the Alfresco Content Services repository easy.
Often Content Management systems provide more capabilities out of the box than most users need;
providing too many capabilities to these users prevents them from working efficiently,
so they may end up using unsanctioned file management solutions which presents a proliferation of content storage
and collaboration solutions as well as compliance issues for organizations.
This application demonstrates how the complexity of Content Management can be simplified
using the Alfresco Application Development Framework to easily and quickly create custom solutions for specific user cases.
### User Interface - layout
There are three main areas of the application controlled by the [Layout component](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/layout):
1. [Application Header](#header)
2. [Side Navigation](#side-navigation)
3. [Document List](#document-list-layout)
![Features](images/features-01.png)
### Header
The application [header](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/header) has three main elements.
1. [Logo and Color](#logo-and-color)
2. [Search](#search)
3. [Current User](#current-user)
![Header](images/header.png)
#### Logo and Color
Logo & app primary color - logo and color are configurable by updating the
[app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) file in the root folder of the project.
Please refer to the [Application Configuration](/getting-started/configuration) documentation for more information on how to change the logo and color.
#### Search
The application [Search](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search) -
uses the [ADF Search Component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/search).
The app provides a 'live' search feature, where users can open files and folders directly from the Search API results.
![Search Input](images/search.png)
If you type `Enter` in the text input area, you are going to see [Search Results](#search-results) page
with advanced filtering and faceted search.
#### Current User
[Current User](https://github.com/Alfresco/alfresco-content-app/tree/development/src/app/components/current-user) -
displays the user's name, and a menu where users can logout.
Optionally through updating the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json)
a language switching menu can be displayed.
![Current User](images/current-user.png)
### Side Navigation
The application [side navigation](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/sidenav) has two features:
a button menu and navigation links.
![Side Navigation](images/side-nav.png)
#### New button
The New button displays a menu which provides three actions:
- Create a new folder - provides a dialog which allows the creation of a new folder, the folder name is mandatory and the description is optional.
- Upload a file - invokes the operating system file browser and allows a user to select file(s) to upload into their current location in the content repository.
- Upload a folder - invokes the operating system folder browser and allows a user to select a folder to upload to their current location in the content repository.
When an upload starts the [upload component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/upload)
is displayed which shows the user the progress of the uploads they have started.
The upload dialog persists on the screen and can be minimized; users are able to continue using the application whilst uploads are in progress
and uploads can be canceled which will stop uploads in progress or permanently delete already completed uploads.
![Uploader](images/uploader.png)
#### Navigation
The navigation links are configurable via the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json).
Default configuration creates two sections.
See [Navigation](/getting-started/navigation) for more information about configuring the side navigation.
### Document List Layout
The main area of the application is composed of several individual ADF components:
1. [Breadcrumb](https://alfresco.github.io/adf-component-catalog/components/BreadcrumbComponent.html)
2. [Toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html)
3. [Document List](https://alfresco.github.io/adf-component-catalog/components/DocumentListComponent.html)
4. [Pagination](https://alfresco.github.io/adf-component-catalog/components/PaginationComponent.html)
![](images/doclist.png)
The application has seven different Document List views which share commonalities between each view and subtle differences depending on the content being loaded which are explained below.
#### Personal Files
Personal Files retrieves all content from the logged in user's home area (`/User Homes/<username>/`) in the repository;
if the user is admin who does not have a home folder then the repository root folder is shown.
Personal Files is the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component,
using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes).
#### File Libraries
File Libraries retrieves all the sites that the user is a member of including what type of site it is: public, moderated or private.
File Libraries is the [Libraries](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/libraries) component,
using the [Sites API](https://api-explorer.alfresco.com/api-explorer/#/sites).
When a user opens one of their sites then the content for the site's document library is shown.
To display the files and folders from a site (`/Sites/<siteid>/Document Library/`) the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component,
using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes) is used.
#### Shared Files
The Shared Files view aggregates all files that have been shared using the QuickShare feature in the content repository.
The [Shared Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/shared-files) component uses the [shared-links API](https://api-explorer.alfresco.com/api-explorer/#/shared-links)
and includes extra columns to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository and who created the shared link.
#### Recent Files
The Recent Files view shows all the files that have been created or modified within the last 30 days by the current user.
The [Recent Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/recent-files)
component uses the Search API to query SOLR for changes made by the user and includes an extra column to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository.
#### Favorites
The Favorites view shows all files and folders from the content repository that have been marked as a favorite by the current user.
The [Favorites](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/favorites) component uses the
[favorites](https://api-explorer.alfresco.com/api-explorer/#/favorites) API to retrieve all the favorite nodes for the user
and includes an extra column to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository.
#### Trash
The Trash view shows all the items that a user has deleted, admin will see items deleted by all users.
The actions available in this view are Restore and Permanently Delete.
The [Trashcan](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/trashcan) component uses the
[trashcan](https://api-explorer.alfresco.com/api-explorer/#/trashcan) API to retrieve the deleted items
and perform the actions requested by the user and includes an extra column to display where the item was
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository before it was deleted.
#### Search Results
The Search Results view shows the found items for a search query. It has a custom layout template and users can easily browse the results and perform actions on items.
For more information on the [SearchComponent](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search), please also check this [Search Results](#search-results1) section.
#### Actions and the Actions Toolbar
All the views incorporate the [toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html)
component from the Alfresco Application Development Framework.
Actions are displayed in the toolbar when item(s) are selected, or a right click is performed; apart from the Trash view they all display the following actions when the current user has the necessary permissions,
actions are automatically hidden when the user does not have permission.
<table>
<thead>
<th>Action</th>
<th>File</th>
<th>Folder</th>
</thead>
<tbody>
<tr>
<td>View</td>
<td>
Opens the selected file using the <a href="https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/preview" target="_blank">Preview</a> component,
where the file cannot be displayed natively in a browser a PDF rendition is obtained from the repository.
</td>
<td>Not applicable</td>
</tr>
<tr>
<td>Download</td>
<td>Downloads single files to the user's computer, when multiple files are selected they are compressed into a ZIP and then downloaded.</td>
<td>Folders are automatically compressed into a ZIP and then downloaded to the user's computer.</td>
</tr>
<tr>
<td>Edit</td>
<td>Not applicable</td>
<td>The folder name and description can be edited in a dialog.</td>
</tr>
<tr>
<td>Favorite</td>
<td colspan="2">
Toggle the favorite mark on or off for files and folders, when multiple items are selected
and one or more are not favorites then the mark will be toggled on.
</td>
</tr>
<tr>
<td>Copy</td>
<td colspan="2">
Files and folders can be copied to another location in the content repository using the
<a href="https://alfresco.github.io/adf-component-catalog/components/ContentNodeSelectorComponent.html" target="_blank">content-node-selector</a> component;
once the copy action has completed the user is notified and can undo the action (which permanently deletes the created copies).
</td>
</tr>
<tr>
<td>Move</td>
<td colspan="2">
Files and folders can be moved to another location in the content repository using the
<a href="https://alfresco.github.io/adf-component-catalog/components/ContentNodeSelectorComponent.html" target="_blank">content-node-selector</a> component;
once the move action has completed the user is notified and can undo the action (which moves the items back to the original location).
</td>
</tr>
<tr>
<td>Share</td>
<td>
Create and copy a link to a file that can be shared, the links are accessible without granting permissions to the file, and do not require users to login to the application. Share links can automatically expire based on a date, the minimum expiry date is controlled by the Content Services repository, which is 1 day from the date of creation.
</td>
<td>
Not applicable.
</td>
</tr>
<tr>
<td>Delete</td>
<td colspan="2">
Files and folders can be deleted from their location in the content repository;
once the delete action has completed the user is notified and can undo the action (which restores the items from the trash).
</td>
</tr>
<tr>
<td>Manage Versions</td>
<td>
Versions of files can be viewed, uploaded, restored, downloaded and deleted by using the version manager dialog;
once each action has completed the list of versions is updated according to the change.
</td>
<td>Not applicable</td>
</tr>
<tr>
<td>Permissions</td>
<td>
Permissions on a file can be adjusted as required in a number of ways; disable inheritance from the parent folder, change a user or groups role and grant users/groups access.
</td>
<td>Not available</td>
</tr>
</tbody>
</table>
Besides the actions available in the toolbar users can
* single click on a file to view it, and a folder to open it
* single click an item to select it
* double click on a file to view it, and a folder to open it
### File Viewer
The File Viewer has been created using the [ViewerComponent](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html) from the ADF. The Viewer has four main areas:
![File Viewer](images/File-Viewer.png)
1. [Header & Toolbar](#header-and-toolbar)
2. [Content](#content)
3. [Thumbnails side pane](#thumbnails-side-pane)
4. [Viewer Controls](#viewer-controls)
#### Header and Toolbar
The Header & Toolbar section of the viewer contains a number of features that relate to the file currently being displayed:
- Close 'X' will return the user to the folder that contains the file.
- The name and file type icon is shown in the middle.
- Next and previous buttons will be displayed either side of the file name so that users can navigate to other files in the folder without navigating away from the viewer.
- Finally, on the right hand side an actions toolbar provides users with the ability to download, favorite, move, copy, delete, manage versions and view info panel.
#### Content
The File Viewer consists of four separate views that handle displaying the content based on four types of content, covering various [file/mime](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html#supported-file-formats) types:
- Document View: PDF files are displayed in the application File Viewer, for other document types (DOCX etc) then a PDF rendition is automatically retrieved.
- Image View: JPEG, PNG, GIF, BMP and SVG images are natively displayed in the application File Viewer.
- Media View: MP4, MP3, WAV, OGG and WEBM files are played natively application File Viewer. The File Viewer will download, by default, 50MB of the content at a time to ensure a smooth playback of the content.
- Text View: TXT, XML, JS, HTML, JSON and TS files are natively displayed as text in the application File Viewer.
#### Thumbnails side pane
The Document View includes a thumbnails pane which can be activated by a button in the Viewer Actions toolbar. Clicking on a thumbnail will take a user directly to the selected page and as users scroll through a document the current page is highlighted in the pane.
#### Viewer Controls
At the bottom of the content the Viewer Controls allow users to interact with the content in various ways; the actions available are dependant on the type of content being displayed.
- Document View:
- Activate/Deactivate thumbnails pane
- Previous/Next page
- Jump to page number
- Zoom in/out
- Fit to page
- Print
- Image View:
- Zoom in/out
- Rotate left/right (does not alter content in the repository)
- Reset image
- Print
- Media View:
- Play/pause
- Timeline position
- Toggle audio
- Audio volume
- Full screen
### Info Drawer
The Info Drawer displays node information in the right sidebar panel. It is created by using the [InfoDrawerComponent](https://alfresco.github.io/adf-component-catalog/components/InfoDrawerComponent.html). This info is available for both folder and file nodes.
Currently, there are 2 tabs available: Properties and Versions.
#### Properties tab
The Properties tab displays the node's metadata info by using the [ContentMetadataCardComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataCardComponent.html).
![](images/content-metadata.png)
For more information, please check also the [ContentMetadataComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html).
#### Comments tab
The Comments tab displays all comments made on the selected node in the respoistory by using the [CommentsComponent](https://alfresco.github.io/adf-component-catalog/components/CommentsComponent.html). Users can post new comments that will be displayed immediately.
### Version Manager
The versions of a file can be viewed & managed by using the [VersionManagerComponent](https://alfresco.github.io/adf-component-catalog/components/VersionManagerComponent.html).
There are 2 ways users can access the Version Manager:
1) From the 'Manage Versions' option of the 'More actions' menu (check [Actions and the Actions Toolbar](#actions-and-the-actions-toolbar)):
![Version Manager Menu](images/version-manager-action.png)
![Version Manager Dialog](images/version-manager-dialog.png)
2) From the [Info Drawer](#info-drawer) (the Details right panel):
![Version Manager Inline](images/version-manager-tab.png)
#### Upload new version
A new version for the selected file can be added by using this button. Users can upload a new file version using a file that is does not have the same name, or mime type as the current version, whilst allowing the user to choose the type of version (minor or major) and inputting supporting comments.
Please also check the [UploadVersionButtonComponent](https://alfresco.github.io/adf-component-catalog/components/UploadVersionButtonComponent.html).
#### Actions Menu
Each item in the version list has a couple of actions available: Restore, Download and Delete. These are displayed if user has permission to do that specific action. The 'Download' and 'Delete' can be also disabled from the app.config.
In the app.config.json file, these are the current settings for the ACA version manager:
```json
{
"adf-version-manager": {
"allowComments": true,
"allowDownload": true
}
}
```
Set the allowComments to false if the version comments should not be displayed on the version list.
Clicking to delete a version of a file triggers a confirmation dialog. Please see the [ConfirmDialogComponent](https://alfresco.github.io/adf-component-catalog/components/ConfirmDialogComponent.html) for more info.
### Search Results
Once you type the text in the Search Input component and press `Enter` you are going to see the Search Results page
![Search Results](images/aca-search-results.png)
This page consists of the following ADF components:
- [Search Filter](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-filter.component.md)
- [Search Chip List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-chip-list.component.md)
- [Search Sorting Picker](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-sorting-picker.component.md)
- [Document List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/document-list.component.md) with custom layout template
- [Info Drawer](#info-drawer) with Metadata and [Version Management](#version-manager)
- [Toolbar with basic actions](#actions-and-the-actions-toolbar) like `Preview`, `Download`, `Favorite`, `Copy`, etc.
And also the Info Drawer, Toolbar and Node Selector dialogs for copy and move operations.
- [Application features](/features/): Details of the user interface and app usage.
- [Getting started](/getting-started/): Configuration of the development environment and the app.
- [Extending](/extending/): How to extend the features of the app with your own code.
- [Tutorials](/tutorials/): Exploration of development techniques in depth.
- [Help](/help): Details of further help and support available.
## How to contribute
-2086
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
---
---
# Extending
Learn how to extend the features of the Alfresco Content Application.
## Contents
- [Extensibility features](/extending/extensibility-features)
- [Extension format](/extending/extension-format)
- [Routes](/extending/routes)
- [Components](/extending/components)
- [Actions](/extending/actions)
- [Application actions](/extending/application-actions)
- [Rules](/extending/rules)
- [Application features](/extending/application-features)
- [Registration](/extending/registration)
- [Creating custom evaluators](/extending/creating-custom-evaluators)
- [Tutorials](/extending/tutorials)
- [Redistributable libraries](/extending/redistributable-libraries)
+55
View File
@@ -0,0 +1,55 @@
---
---
# Actions
| Name | Description |
| -- | -- |
| **id** | Unique identifier. |
| **type** | Action type, see [Application Actions](/extending/application-actions) for more details. |
| **payload** | Action payload, a string containing value or expression. |
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"actions": [
{
"id": "plugin1.actions.settings",
"type": "NAVIGATE_URL",
"payload": "/settings"
},
{
"id": "plugin1.actions.info",
"type": "SNACKBAR_INFO",
"payload": "I'm a nice little popup raised by extension."
},
{
"id": "plugin1.actions.node-name",
"type": "SNACKBAR_INFO",
"payload": "$('Action for ' + context.selection.first.entry.name)"
}
]
}
```
## Value expressions
You can use light-weight expression syntax to provide custom parameters for the action payloads.
```text
$(<expression>)
```
Expressions are valid JavaScript blocks that evaluate to values.
Examples:
```text
$('hello world') // 'hello world'
$('hello' + ', ' + 'world') // 'hello, world'
$(1 + 1) // 2
$([1, 2, 1 + 2]) // [1, 2, 3]
```
+116
View File
@@ -0,0 +1,116 @@
---
---
# Application Actions
Application is using NgRx (Reactive libraries for Angular, inspired by Redux).
To get more information on NxRx please refer to the following resources:
- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
Most of the application features are already exposed in the form of NgRx Actions and corresponding Effects.
You can invoke any action via a single `Store` dispatcher, similar to the following:
```ts
export class MyComponent {
constructor(private store: Store<AppStore>) {}
onClick() {
this.store.dispatch(new SearchByTermAction('*'));
}
}
```
The code above demonstrates a simple 'click' handler that invokes `Search by Term` feature
and automatically redirects user to the **Search Results** page.
Another example demonstrates viewing a node from a custom application service API:
```ts
export class MyService {
constructor(private store: Store<AppStore>) {}
viewFile(node: MinimalNodeEntity) {
this.store.dispatch(new ViewFileAction(node));
}
}
```
## Using with Extensions
You can invoke every application action from the extensions, i.e. buttons, menus, etc.
<p class="tip">
Many of the actions take currently selected nodes if no payload provided.
That simplifies declaring and invoking actions from the extension files.
</p>
In the example below, we create a new entry to the "NEW" menu dropdown
and provide a new `Create Folder (plugin1)` command that invokes the `CREATE_FOLDER` application action.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"create": [
{
"id": "plugin1.create.folder",
"type": "default",
"icon": "create_new_folder",
"title": "Create Folder (plugin1)",
"actions": {
"click": "CREATE_FOLDER"
}
}
]
}
}
```
The `CREATE_FOLDER` action will trigger corresponding NgRx Effects to show the dialog
and perform document list reload if needed.
Below is the list of public actions types you can use in the plugin definitions as a reference to the action:
| Name | Payload | Description |
| -- | -- | -- |
| SET_CURRENT_FOLDER | Node | Notify components about currently opened folder. |
| SET_CURRENT_URL | string | Notify components about current browser URL. |
| SET_USER_PROFILE | Person | Assign current user profile. |
| TOGGLE_INFO_DRAWER | n/a | Toggle info drawer for the selected node. |
| ADD_FAVORITE | MinimalNodeEntity[] | Add nodes (or selection) to favorites. |
| REMOVE_FAVORITE | MinimalNodeEntity[] | Removes nodes (or selection) from favorites. |
| DELETE_LIBRARY | string | Delete a Library by id. Takes selected node if payload not provided. |
| CREATE_LIBRARY | n/a | Invoke a "Create Library" dialog. |
| SET_SELECTED_NODES | MinimalNodeEntity[] | Notify components about selected nodes. |
| DELETE_NODES | MinimalNodeEntity[] | Delete the nodes (or selection). Supports undo actions. |
| UNDO_DELETE_NODES | any[] | Reverts deletion of nodes (or selection). |
| RESTORE_DELETED_NODES | MinimalNodeEntity[] | Restores deleted nodes (or selection). Typically used with Trashcan. |
| PURGE_DELETED_NODES | MinimalNodeEntity[] | Permanently delete nodes (or selection). Typically used with Trashcan. |
| DOWNLOAD_NODES | MinimalNodeEntity[] | Download nodes (or selections). Creates a ZIP archive for folders or multiple items. |
| CREATE_FOLDER | string | Invoke a "Create Folder" dialog for the opened folder (or the parent folder id in the payload). |
| EDIT_FOLDER | MinimalNodeEntity | Invoke an "Edit Folder" dialog for the node (or selection). |
| SHARE_NODE | MinimalNodeEntity | Invoke a "Share" dialog for the node (or selection). |
| UNSHARE_NODES | MinimalNodeEntity[] | Remove nodes (or selection) from the shared nodes (does not remove content). |
| COPY_NODES | MinimalNodeEntity[] | Invoke a "Copy" dialog for the nodes (or selection). Supports undo actions. |
| MOVE_NODES | MinimalNodeEntity[] | Invoke a "Move" dialog for the nodes (or selection). Supports undo actions. |
| MANAGE_PERMISSIONS | MinimalNodeEntity | Invoke a "Manage Permissions" dialog for the node (or selection). |
| MANAGE_VERSIONS | MinimalNodeEntity | Invoke a "Manage Versions" dialog for the node (or selection). |
| NAVIGATE_URL | string | Navigate to a given route URL within the application. |
| NAVIGATE_ROUTE | any[] | Navigate to a particular Route (supports parameters). |
| NAVIGATE_FOLDER | MinimalNodeEntity | Navigate to a folder based on the Node properties. |
| NAVIGATE_PARENT_FOLDER | MinimalNodeEntity | Navigate to a containing folder based on the Node properties. |
| NAVIGATE_LIBRARY | string | Navigate to library. |
| SEARCH_BY_TERM | string | Perform a simple search by the term and navigate to Search results. |
| SNACKBAR_INFO | string | Show information snackbar with the message provided. |
| SNACKBAR_WARNING | string | Show warning snackbar with the message provided. |
| SNACKBAR_ERROR | string | Show error snackbar with the message provided. |
| UPLOAD_FILES | n/a | Invoke "Upload Files" dialog and upload files to the currently opened folder. |
| UPLOAD_FOLDER | n/a | Invoke "Upload Folder" dialog and upload selected folder to the currently opened one. |
| VIEW_FILE | MinimalNodeEntity | Preview the file (or selection) in the Viewer. |
| PRINT_FILE | MinimalNodeEntity | Print the file opened in the Viewer (or selected). |
| FULLSCREEN_VIEWER | n/a | Enters fullscreen mode to view the file opened in the Viewer. |
| LOGOUT | n/a | Log out and redirect to Login screen. |
+637
View File
@@ -0,0 +1,637 @@
---
---
# Application Features
This section contains application-specific features that may vary depending on the final implementation.
The ACA supports the following set of extension points:
- Create menu
- Navigation Bar
- Toolbar
- Context Menu
- Viewer
- Sidebar (aka Info Drawer)
- Content metadata presets (for `Properties` tab)
All the customizations are stored in the `features` section of the configuration file:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"create": [],
"navbar": [],
"toolbar": [],
"contextMenu": [],
"viewer": {
"toolbarActions:": [],
"toolbarMoreMenu:": [],
"openWith": [],
"content": []
},
"sidebar": [],
"content-metadata-presets": []
}
}
```
Other applications or external plugins can utilise different subsets of the configuration above.
Also, extra entries can be added to the configuration schema.
## Content Actions
Most of the UI elements that operate with content, like toolbar buttons or menus,
are based on `ContentActionRef` interface implementation:
```ts
interface ContentActionRef {
id: string;
type: ContentActionType;
title?: string;
description?: string;
order?: number;
icon?: string;
disabled?: boolean;
children?: Array<ContentActionRef>;
component?: string;
actions?: {
click?: string;
[key: string]: string;
};
rules?: {
enabled?: string;
visible?: string;
[key: string]: string;
};
}
```
You can define content actions in the `app.extensions.json` file using the structure above.
## Create Menu
Provides extension endpoint for the "NEW" menu options.
You can populate the menu with an extra entries like in the example below:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"create": [
{
"id": "plugin1.create.folder",
"icon": "create_new_folder",
"title": "Create Folder (plugin1)",
"actions": {
"click": "CREATE_FOLDER"
},
"rules": {
"enabled": "app.navigation.folder.canCreate"
}
},
{
"id": "plugin1.create.uploadFile",
"icon": "file_upload",
"title": "Upload Files (plugin1)",
"actions": {
"click": "UPLOAD_FILES"
},
"rules": {
"enabled": "app.navigation.folder.canUpload"
}
}
]
}
}
```
Please refer to the [Content Actions](/extending/application-features#content-actions) section for more details on supported properties.
<p class="tip">
It is also possible to update or disable existing entries from within the external extension files. You will need to know the `id` of the target element to customize.
</p>
## Navigation Bar
The Navigation bar consists of Link elements (`NavBarLinkRef`) organized into Groups (`NavBarGroupRef`).
```ts
export interface NavBarGroupRef {
id: string;
items: Array<NavBarLinkRef>;
order?: number;
disabled?: boolean;
}
export interface NavBarLinkRef {
id: string;
icon: string;
title: string;
route: string;
url?: string; // evaluated at runtime based on route ref
description?: string;
order?: number;
disabled?: boolean;
}
```
Your extensions can perform the following actions at runtime:
- Register new groups with links
- Insert new links into existing groups
- Update properties of the existing links
- Disable existing links or entire groups
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"navbar": [
{
"id": "app.navbar.primary",
"items": [
{
"id": "app.navbar.personalFiles",
"icon": "folder",
"title": "Personal Files",
"route": "personal-files"
},
{
"id": "app.navbar.libraries",
"icon": "group_work",
"title": "Libraries",
"route": "libraries"
}
]
},
{
"id": "app.navbar.secondary",
"items": [
{
"id": "app.navbar.shared",
"icon": "people",
"title": "Shared",
"route": "shared"
}
]
}
]
}
}
```
## Sidebar (Info Drawer)
You can provide the following customizations for the Sidebar (aka Info Drawer) component:
- Add extra tabs with custom components
- Disable tabs from the main application or extensions
- Replace content or properties of existing tabs
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"sidebar": [
{
"id": "app.sidebar.properties",
"order": 100,
"title": "Properties",
"component": "app.components.tabs.metadata"
},
{
"id": "app.sidebar.comments",
"order": 200,
"title": "Comments",
"component": "app.components.tabs.comments"
}
]
}
}
```
The example above renders two tabs:
- `Properties` tab that references the `app.components.tabs.metadata` component
- `Comments` tab that references the `app.components.tabs.comments` component
All corresponding components must be registered for runtime use.
<p class="tip">
See the [Registration](/extending/registration) section for more details
on how to register your own entries to be re-used at runtime.
</p>
### Tab properties
| Name | Description |
| ------------- | ----------------------------------------------------------- |
| **id** | Unique identifier. |
| **component** | The main [component](/extending/components) to use for the route. |
| **title** | Tab title or resource key. |
| icon | Tab icon |
| disabled | Toggles disabled state. Can be assigned from other plugins. |
| order | The order of the element. |
### Tab components
Every component you assign for the tab content receives the following additional properties at runtime:
| Name | Type | Description |
| ---- | ---------------------- | --------------------------- |
| node | MinimalNodeEntryEntity | Node entry to be displayed. |
## Toolbar
The toolbar extension point is represented by an array of Content Action references.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"toolbar": [
{
"id": "app.toolbar.preview",
"title": "View",
"icon": "open_in_browser",
"actions": {
"click": "VIEW_FILE"
},
"rules": {
"visible": "app.toolbar.canViewFile"
}
},
{
"id": "app.toolbar.download",
"title": "Download",
"icon": "get_app",
"actions": {
"click": "DOWNLOAD_NODES"
},
"rules": {
"visible": "app.toolbar.canDownload"
}
}
]
}
}
```
The content actions are applied to the toolbars for the following Views:
- Personal Files
- Libraries
- Shared
- Recent Files
- Favorites
- Trash
- Search Results
- Libraries Search Results
## Context Menu
Context Menu extensibility is similar to the one of the Toolbar.
You may want to define a list of content actions backed by Rules and wired with Application Actions.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"contextMenu": [
{
"id": "app.context.menu.download",
"order": 100,
"title": "Download",
"icon": "get_app",
"actions": {
"click": "DOWNLOAD_NODES"
},
"rules": {
"visible": "app.toolbar.canDownload"
}
}
]
}
}
```
Note, you can re-use any rules and evaluators that are available.
In the example above, the context menu action `Download` utilizes the `app.toolbar.canDownload` rule,
declared in the `rules` section:
```json
{
"rules": [
{
"id": "app.toolbar.canDownload",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.canDownload" },
{ "type": "rule", "value": "app.navigation.isNotTrashcan" }
]
}
]
}
```
## Viewer
Viewer component in ACA supports the following extension points:
- Content Viewers
- Toolbar actions
- `More` toolbar actions
- `Open With` actions
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"viewer": {
"content": [],
"toolbarActions:": [],
"toolbarMoreMenu:": [],
"openWith": []
}
}
}
```
### Content View
You can provide custom components that render a particular type of the content based on extensions.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"viewer": {
"content": [
{
"id": "app.viewer.pdf",
"fileExtension": "pdf",
"component": "app.components.tabs.metadata"
},
{
"id": "app.viewer.docx",
"fileExtension": "docx",
"component": "app.components.tabs.comments"
}
]
}
}
}
```
In the example above we replace `PDF` view with the `metadata` tab
and `DOCX` view with the `comments` tab.
Every custom component receives the following properties at runtime:
| Name | Type | Description |
| --------- | ---------------------- | --------------------------- |
| node | MinimalNodeEntryEntity | Node entry to be displayed. |
| url | string | File content URL. |
| extension | string | File name extension. |
### Toolbar actions
The default toolbar actions from the ACA viewer can be customized through extensions to be replaced, modified or disabled.
New viewer toolbar actions can also be added from the extensions config:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"viewer": {
"toolbarActions": [
{
"id": "app.viewer.versions",
"order": 500,
"title": "APP.ACTIONS.VERSIONS",
"icon": "history",
"actions": {
"click": "MANAGE_VERSIONS"
},
"rules": {
"visible": "app.toolbar.versions"
}
}
],
"toolbarMoreMenu": [...]
}
}
}
```
The ADF Viewer component allows you to provide custom entries for the `More` menu button on the toolbar.
The ACA provides an extension point for this menu that you can utilize to populate custom menu items:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"viewer": {
"toolbarActions": [...],
"toolbarMoreMenu": [
{
"id": "app.viewer.share",
"order": 300,
"title": "Share",
"icon": "share",
"actions": {
"click": "SHARE_NODE"
},
"rules": {
"visible": "app.selection.file.canShare"
}
}
]
}
}
}
```
### Open With actions
You can provide a list of `Open With` actions to render with every instance of the Viewer.
In the following example, we create a simple `Snackbar` action reference,
and invoke it from the custom `Open With` menu entry called `Snackbar`.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"actions": [
{
"id": "plugin1.actions.info",
"type": "SNACKBAR_INFO",
"payload": "I'm a nice little popup raised by extension."
}
],
"features": {
"viewer": {
"openWith": [
{
"id": "plugin1.viewer.openWith.action1",
"type": "button",
"icon": "build",
"title": "Snackbar",
"actions": {
"click": "plugin1.actions.info"
}
}
]
}
}
}
```
As with other content actions, custom plugins can disable, update or extend `Open With` actions.
## Content metadata presets
The content metadata presets are needed by the [Content Metadata Component](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html#readme) to render the properties of metadata aspects for a given node.
The different aspects and their properties are configured in the `app.config.json` file, but they can also be set on runtime through extension files.
Configuring these presets from `app.extensions.json` will overwrite the default application setting.
Settings them from custom plugins allows user to disable, update or extend these presets.
Check out more info about merging extensions [here](/extending/extension-format#merging-properties).
The `content-metadata-presets` elements can be switched off by setting the `disabled` property.
This can be applied also for nested items, allowing disabling down to aspect level.
<p class="tip">
In order to modify or disable existing entries, you need to know the id of the target element, along with its parents ids.
</p>
Your extensions can perform the following actions at runtime:
* Add new presets items.
* Add new items to existing presets at any level.
* Disable specific items down to the aspect level.
* Modify any existing item based on id.
Regarding properties, you can either:
* Add new properties to existing aspect, or
* Redefine the properties of an aspect.
Review this code snippet to see how you can overwrite the properties for `exif:exif` aspect from an external plugin:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"content-metadata-presets": [
{
"id": "app.content.metadata.custom",
"custom": [
{
"id": "app.content.metadata.customGroup",
"items": [
{
"id": "app.content.metadata.exifAspect",
"disabled": true
},
{
"id": "app.content.metadata.exifAspect2",
"aspect": "exif:exif",
"properties": [
"exif:orientation",
"exif:manufacturer",
"exif:model",
"exif:software"
]
}
]
}
]
}
]
}
}
```
This external plugin disables the initial `exif:exif` aspect already defined in the `app.extensions.json` and defines other properties for the `exif:exif` aspect.
Here is the initial setting from `app.extension.json`:
```json
...
"content-metadata-presets": [
{
"id": "app.content.metadata.custom",
"custom": [
{
"id": "app.content.metadata.customGroup",
"title": "APP.CONTENT_METADATA.EXIF_GROUP_TITLE",
"items": [
{
"id": "app.content.metadata.exifAspect",
"aspect": "exif:exif",
"properties": [
"exif:pixelXDimension",
"exif:pixelYDimension",
"exif:dateTimeOriginal",
"exif:exposureTime",
"exif:fNumber",
"exif:flash",
"exif:focalLength",
"exif:isoSpeedRatings",
"exif:orientation",
"exif:manufacturer",
"exif:model",
"exif:software"
]
}
]
}
]
}
]
...
```
<p class="tip">
In order to allow the content-metadata presets to be extended, the settings from `app.config.json` must be copied to the `app.extensions.json` file and its ids must be added to all the items.
Having ids allows external plugins to extend the current setting.
</p>
+26
View File
@@ -0,0 +1,26 @@
---
---
# Components
You can register any Angular component to participate in extensibility.
The components are used to create custom:
- routes and pages
- toolbar buttons
- menu items
| Key | Type | Description |
| --------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. |
| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
| app.toolbar.toggleFavoriteLibrary | ToggleFavoriteLibraryComponent | The toolbar button component that toggles Favorite library state for the selection. |
| app.toolbar.toggleJoinLibrary | ToggleJoinLibraryComponent | The toolbar button component that toggles Join/Cancel Join request for the selected library. |
See [Registration](/extending/registration) section for more details
on how to register your own entries to be re-used at runtime.
Note that custom extensions can also replace any existing component at runtime by a known identifier,
besides registering a new one.
@@ -0,0 +1,68 @@
---
---
# Creating custom evaluators
Rule evaluators are plain JavaScript (or TypeScript) functions that take `RuleContext` references and an optional list of `RuleParameter` instances.
Application provides a special [RuleEvaluator](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L30) type alias for evaluator functions:
```ts
export type RuleEvaluator = (context: RuleContext, ...args: any[]) => boolean;
```
Create a function that is going to check if a user has selected one or multiple nodes.
```ts
export function hasSelection(
context: RuleContext,
...args: RuleParameter[]
): boolean {
return !context.selection.isEmpty;
}
```
The `context` is a reference to a special instance of the [RuleContext](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app/extensions/rule.extensions.ts#L32) type,
that provides each evaluator access to runtime entities.
```ts
export interface RuleContext {
selection: SelectionState;
navigation: NavigationState;
permissions: NodePermissions;
getEvaluator(key: string): RuleEvaluator;
}
```
The `SelectionState` interface exposes information about the global selection state:
```ts
export interface SelectionState {
count: number;
nodes: MinimalNodeEntity[];
libraries: SiteEntry[];
isEmpty: boolean;
first?: MinimalNodeEntity;
last?: MinimalNodeEntity;
folder?: MinimalNodeEntity;
file?: MinimalNodeEntity;
library?: SiteEntry;
}
```
Next, register the function you have created earlier with the `ExtensionService` and give it a unique identifier:
```ts
extensions.setEvaluators({
'plugin1.rules.hasSelection': hasSelection
});
```
Now, the `plugin1.rules.hasSelection` evaluator can be used as an inline rule reference,
or part of the composite rule like `core.every`.
<p class="tip">
See the [Registration](/extending/registration) section for more details
on how to register your own entries to be re-used at runtime.
</p>
+34
View File
@@ -0,0 +1,34 @@
---
---
# Extensibility features
Application extensibility is performed via the root `/src/assets/app.extensions.json`,
and any number of external plugins that are references of the main entry point.
The application also comes with the `/src/assets/plugins/` folder
already preconfigured to store external files.
You can create plugins that change, toggle, or extend the following areas:
- Navigation sidebar links and groups
- Context Menu
- Sidebar (aka Info Drawer)
- Toolbar entries
- buttons
- menu buttons
- separators
- Viewer actions
- "Open With" entries
- toolbar entries
- buttons
- "More actions" buttons
- Content metadata presets (used on `Properties` tab)
- Custom icons
Extensions can also:
- Overwrite or disable extension points of the main application or other plugins
- Change rules, actions or any visual element
- Register new application routes based on empty pages or layouts
- Register new rule evaluators, components, guards
+319
View File
@@ -0,0 +1,319 @@
---
---
# Extension format
The format is represented by a JSON file with the structure similar to the following:
```json
{
"$id": "unique.id",
"$name": "extension.name",
"$version": "1.0.0",
"$vendor": "author.name",
"$license": "license",
"$runtime": "1.5.0",
"$description": "some description",
"routes": [],
"actions": [],
"rules": [],
"features": {}
}
```
## Schema
You can find the JSON schema at the project root folder: [extension.schema.json](https://github.com/Alfresco/alfresco-content-app/blob/master/extension.schema.json).
<p class="tip">
The Schema allows you to validate extension files, provides code completion and documentation hints.
</p>
```json
{
"$schema": "../../extension.schema.json",
"$name": "app",
"$version": "1.0.0"
}
```
## Multiple files
You can have multiple extension files distributed separately.
All additional files are linked via the `$references` property.
The order of declaration defines the order of loading.
```json
{
"$schema": "../../extension.schema.json",
"$name": "app",
"$version": "1.0.0",
"$references": ["plugin1.json", "plugin2.json"]
}
```
<p class="warning">
All extension files are merged together at runtime.
This allows plugins to overwrite the code from the main application or to alter other plugins.
</p>
## Startup behavior
First, the root `app.extensions.json` is loaded by means of the special `Loader` service.
The file can contain all the necessary declarations for an application to function. Extra plugin files are fully optional.
Next, the `Loader` traverses the `$references` metadata and loads additional files if provided.
For the sake of speed the files are loaded in parallel, however once everything is loaded, they are applied in the order of declaration.
After all the external files are fetched, the `Loader` sorts them, removes the metadata properties and stacks the resulting JSON objects on top of each other.
<p class="tip">
Any top-level property name that starts with the `$` symbol is considered metadata and does not participate in the merge process.
That allows a plugin to carry extra information for maintenance and visualisation purposes, for example: `$name`, `$version`, `$description`, `$license`, etc.
</p>
### Merging properties
There are no limits in the JSON structure and level of nesting.
All objects are merged into a single set based on property keys and object IDs (for arrays).
Before: Plugin 1
```json
{
"$name": "plugin1",
"plugin1.key": "value",
"plugin1.text": "string"
}
```
Before: Plugin 2
```json
{
"$name": "plugin2",
"plugin2.key": "value",
"plugin1.text": "custom string"
}
```
Final result:
```json
{
"plugin1.key": "value",
"plugin1.text": "custom string",
"plugin2.key": "value"
}
```
Note that as a result we have two unique properties `plugin1.key` and `plugin2.key`,
and also a `plugin1.text` that was first defined in the `Plugin 1`, but then overwritten by the `Plugin 2`.
<p class="tip">
JSON merging is a very powerful concept as it gives you the ability to alter any base application settings,
or toggle features in other plugins without rebuilding the application or corresponding plugin libraries.
</p>
### Merging objects
The complex objects are merged by properties. This process is recursive and has no limits for nesting levels.
Before: Plugin 1
```json
{
"$name": "plugin1",
"features": {
"title": "some title",
"page1": {
"title": "page 1"
}
}
}
```
Before: Plugin 2
```json
{
"$name": "plugin2",
"features": {
"page1": {
"title": "custom title"
},
"page2": {
"title": "page 2"
}
}
}
```
Final result:
```json
{
"features": {
"title": "some title",
"page1": {
"title": "custom title"
},
"page2": {
"title": "page 2"
}
}
}
```
You can see the unique properties get merged together in a single object.
However the last non-unique property overwrites the previous value.
Using the current design it is not possible to delete any application property from the plugin.
The loader engine only supports overwriting values. Many components however support the `disabled` property you can change using an external definition:
Before: Plugin 1
```json
{
"$name": "plugin1",
"feature1": {
"disabled": false,
"text": "some-feature",
"icon": "some-icon"
}
}
```
Before: Plugin 2
```json
{
"$name": "plugin2",
"feature1": {
"disabled": true
}
}
```
Final result:
```json
{
"feature1": {
"disabled": true,
"text": "some-feature",
"icon": "some-icon"
}
}
```
You can find more details in the [Disabling Content](/extending/extension-format#disabling-content) section
### Merging arrays
The extension `Loader` provides a special support for merging Arrays.
By default, two collections will be merged into a single array unless objects have `id` properties.
<p class="tip">
If array contains two objects with the same `id` property, the objects will be merged rather than appended.
</p>
Before: Plugin 1
```json
{
"$name": "plugin1",
"features": [
{ "text": "common 1" },
{
"id": "page1",
"text": "page 1"
}
]
}
```
Before: Plugin 2
```json
{
"$name": "plugin2",
"features": [
{ "text": "common 2" },
{
"id": "page1",
"text": "custom page"
}
]
}
```
Final result:
```json
{
"features": [
{ "text": "common 1" },
{ "text": "common 2" },
{
"id": "page1",
"text": "custom page"
}
]
}
```
Note that objects with the same `page1` identifiers were merged while other unique entries were appended to the resulting array.
## Disabling content
Most of the schema elements can be switched off by using the `disabled` property:
```json
{
"$schema": "../../extension.schema.json",
"$name": "app",
"$version": "1.0.0",
"features": {
"create": [
{
"id": "app.create.folder",
"disabled": true,
"order": 100,
"icon": "create_new_folder",
"title": "Create Folder"
}
]
}
}
```
This feature becomes handy when you want to disable existing functionality from within the external plugin.
In the example below, the plugin called `plugin1` replaces standard `app.create.folder` menu
exposed by the application with a custom one coming with the plugin.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"create": [
{
"id": "app.create.folder",
"disabled": true,
...
},
{
"id": "plugin1.create.folder",
"title": "Create Folder",
...
}
]
}
}
```
+42
View File
@@ -0,0 +1,42 @@
# Custom Icons
You can register and use custom `.svg` icons with toolbars, context menus, etc.
The icons are declared in the `features.icons` section, for example:
```json
{
"features": {
"icons": [
{
"id": "adf:join_library",
"value": "./assets/images/join-library.svg"
},
{
"id": "adf:move_file",
"value": "./assets/images/adf-move-file-24px.svg"
}
]
}
}
```
The `id` value must conform to the format `[namespace]:[name]`,
similar to that of the [Material Icon](https://material.angular.io/components/icon/api) component.
The icon file path should be relative to the deployed application root (or `index.html` file);
After that, you can use the icon id with other elements, for example:
```json
{
"id": "app.toolbar.move",
"order": 500,
"title": "APP.ACTIONS.MOVE",
"icon": "adf:move_file",
"actions": {
"click": "MOVE_NODES"
}
}
```
It is also possible to override the icon value or disable the entry from within external extensions.
+227
View File
@@ -0,0 +1,227 @@
---
---
# Redistributable libraries
Extension libraries are based on the standard Angular libraries and definition files in the JSON format.
Please read more details in the following article: [Library support in Angular CLI 6](https://github.com/angular/angular-cli/wiki/stories-create-library#library-support-in-angular-cli-6)
See also
- The Angular Library Series - Creating a Library with the Angular CLI
- Part 1: https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5
- Part 2: https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121
## Creating extension library
First, generate a new project within the workspace:
```sh
ng generate library my-extension
```
You will get a new project in the `projects/my-extensions` folder.
By default, the project contains at least the following content:
- Example component `my-extension.component.ts`
- Example service `my-extension.service.ts`
- Angular Module example `my-extension.module.ts`
Next, build the project with the following command:
```sh
ng build my-extension
```
Angular CLI automatically configures Typescript path mappings for the project, so that you do not need any additional steps to link the library.
### Register dynamic components
Update `my-extension.module.ts` and put all the content you plan to use at runtime dynamically to the `entryComponents` section of the module.
```typescript
@NgModule({
imports: [],
declarations: [MyExtensionComponent],
exports: [MyExtensionComponent],
entryComponents: [MyExtensionComponent]
})
export class MyExtensionModule {}
```
Now we need to register `MyExtensionComponent` as an extension component.
Update the code as in the next example:
```typescript
import { ExtensionService } from '@alfresco/adf-extensions';
@NgModule({...})
export class MyExtensionModule {
constructor(extensions: ExtensionService) {
extensions.setComponents({
'my-extension.main.component': MyExtensionComponent,
});
}
}
```
Now you can use the `my-extension.main.component` identifier in the JSON definitions
if you want to reference the `MyExtensionComponent`.
### Plugin definition file
Create a new `assets/my-extension.json` file in the library project root folder with the following content:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"$description": "demo plugin",
"routes": [
{
"id": "my.extension.route",
"path": "ext/my/route",
"component": "my-extension.main.component"
}
],
"features": {
"navbar": [
{
"id": "my.extension.nav",
"items": [
{
"id": "my.extension.main",
"icon": "extension",
"title": "My Extension",
"route": "my.extension.route"
}
]
}
]
}
}
```
Update the root `package.json` file and append the following entry to the `scripts` section:
```json
{
"scripts": {
...,
"build:my-extension":
"ng build my-extension && cpr projects/my-extension/assets dist/my-extension/assets --deleteFirst"
}
}
```
You can now use that script to build the library and copy assets to the output folder.
<p class="tip">
It is good practice to provide installation instructions for your library in the `README.md` file.
Be sure to mention that developers should have a build rule to copy your plugin definition file to the `assets/plugins` folder of the main application.
</p>
## Publishing library to NPM
Before you publish you should always rebuild the library:
```sh
npm run build:my-extension
```
Go to the output folder and run the publish command.
```sh
cd dist/my-extension
npm publish
```
Note, you are required to have a valid [NPM](https://www.npmjs.com/) account.
See more details in the [Publishing your library](https://github.com/angular/angular-cli/wiki/stories-create-library#publishing-your-library) article.
## Consuming extension library
Assuming you have published your extension library to NPM, you can install it using the standard command:
```sh
npm install my-extension
```
This installs the library and all its dependencies.
<p class="warning">
You do not need to install the library in the original workspace as the application is already configured to use the local version from the `dist` folder.
</p>
### Copy assets
Edit the `angular.json` configuration file and add the following rule if you develop and test extension libraries in the same workspace.
```json
{
"glob": "**/*.json",
"input": "dist/my-extension/assets",
"output": "/assets/plugins"
}
```
Use the following rule if you are installing an extension from NPM:
```json
{
"glob": "**/*.json",
"input": "node_modules/my-extension/assets",
"output": "/assets/plugins"
}
```
### Register module
In the main application, edit the `src/app/extensions.module.ts` file and append the module declaration as in the next example:
```typescript
...
import { MyExtensionModule } from 'my-extension';
@NgModule({
...
imports: [
...,
MyExtensionModule
]
})
export class AppExtensionsModule {}
```
### Register plugin
Finally, update the `assets/app.extensions.json` file and add a reference to the new plugin:
```json
{
"$references": [
...,
"my-extension.json"
]
}
```
## Testing library
Run the application and ensure you have an extra navigation sidebar entry:
```sh
npm start
```
Click the `My Extension` link and in the main content area you will see the extension component coming from your library.
<p class="warning">
Depending on the application setup, you may need enabling external plugins via the `Settings` dialog available for `admin` users (clicking the application profile button).
</p>
+53
View File
@@ -0,0 +1,53 @@
---
---
# Registration
You can use `ExtensionService` to register custom components, authentication guards,
rule evaluators, etc.
It is recommended to register custom content from within the module constructor.
In that case all plugins will be available right after the main application component is ready.
Update the main application module `app.module.ts`, or create your own module,
and use the following snippet to register custom content:
```ts
import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions';
@NgModule({
imports: [ ExtensionsModule.forChild() ]
declarations: [ MyComponent1, MyLayout ],
entryComponents: [ MyComponent1, MyLayout ]
})
export class MyExtensionModule {
constructor(extensions: ExtensionService) {
extensions.setComponents({
'plugin1.components.my': MyComponent1,
'plugin1.layouts.my': MyLayout
});
extensions.setAuthGuards({
'plugin.auth': MyAuthGuard
});
extensions.setEvaluators({
'plugin1.rules.custom1': MyCustom1Evaluator,
'plugin1.rules.custom2': MyCustom2Evaluator
});
}
}
```
Use `ExtensionsModule.forChild()` when importing into the child modules,
and `ExtensionsModule.forRoot()` for the main application module.
<p class="warning">
According to Angular rules, all components that are created dynamically at runtime
need to be registered within the `entryComponents` section of the NgModule.
</p>
The Registration API is not limited to the custom content only.
You can replace any existing entries by replacing the values from your module.
+80
View File
@@ -0,0 +1,80 @@
---
---
# Routes
To create a new route, populate the `routes` section with the corresponding entries.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [
{
"id": "plugin1.routes.bin",
"path": "ext/bin",
"layout": "app.layout.main",
"component": "your.component.id"
}
]
}
```
## Route properties
| Name | Description |
| ------------- | -------------------------------------------------------------------------------------- |
| **id** | Unique identifier. |
| **path** | Runtime path of the route. |
| **component** | The main [component](/extending/components) to use for the route. |
| layout | The layout [component](/extending/components) to use for the route. |
| auth | List of [authentication guards](/extending/routes#authentication-guards). Defaults to `[ "app.auth" ]`. |
| data | Custom property bag to carry with the route. |
Use the `app.layout.main` value for the `layout` property to get the default application layout,
with header, navigation sidebar and main content area.
You can register any component to back the `app.layout.main` value.
<p class="tip">
By default, the `app.layout.main` is used if you do not specify any custom values.
Use `blank` if you want your route component take the whole page.
</p>
You can define the full route schema like in the next example:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [
{
"id": "plugin1.routes.bin",
"path": "ext/bin",
"component": "your.component.id",
"layout": "app.layout.main",
"auth": ["app.auth"],
"data": {
"title": "Custom Trashcan"
}
}
]
}
```
All application routes require at least one authentication guard.
Defaults to the `['app.auth']` value.
## Authentication Guards
Below is the list of the authentication guards main application registers on startup.
| Key | Type | Description |
| -------- | ------------ | ------------------------------------------------------------------------- |
| app.auth | AuthGuardEcm | ADF guard, validates ACS authentication and redirects to Login if needed. |
You can refer those guards from within your custom extensions,
or [register](/extending/registration) your custom implementations.
+215
View File
@@ -0,0 +1,215 @@
---
---
# Rules
Rules allow evaluating conditions for extension components.
For example, you can disable or hide elements based on certain rules.
Every rule is backed by a condition evaluator.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"rules": [
{
"id": "app.trashcan",
"type": "app.navigation.isTrashcan"
}
]
}
```
Rules can accept other rules as parameters:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"rules": [
{
"id": "app.toolbar.favorite.canAdd",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.canAddFavorite" },
{ "type": "rule", "value": "app.navigation.isNotRecentFiles" },
{ "type": "rule", "value": "app.navigation.isNotSharedFiles" },
{ "type": "rule", "value": "app.navigation.isNotSearchResults" }
]
}
]
}
```
<p class="tip">
You can also negate any rule by utilizing a `!` prefix:
`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`.
</p>
It is also possible to use inline references to registered evaluators without declaring rules,
in case you do not need providing extra parameters, or chaining multiple rules together.
## Core Evaluators
You can create new rules by chaining other rules and evaluators.
| Key | Description |
| ---------- | ----------------------------------------------------------------------------- |
| core.every | Evaluates to `true` if all chained rules evaluate to `true`. |
| core.some | Evaluates to `true` if at least one of the chained rules evaluates to `true`. |
| core.not | Evaluates to `true` if all chained rules evaluate to `false`. |
Below is an example of the composite rule definition that combines the following conditions:
- user has selected a single file
- user is not using the **Trashcan** page
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"rules": [
{
"id": "app.toolbar.canViewFile",
"type": "core.every",
"parameters": [
{
"type": "rule",
"value": "app.selection.file"
},
{
"type": "rule",
"value": "core.not",
"parameters": [
{
"type": "rule",
"value": "app.navigation.isTrashcan"
}
]
}
]
}
]
}
```
You can now declare a toolbar button action that is based on the rule above.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"features": {
"toolbar": [
{
"id": "app.toolbar.preview",
"type": "button",
"title": "View File",
"icon": "open_in_browser",
"actions": {
"click": "VIEW_FILE"
},
"rules": {
"visible": "app.toolbar.canViewFile"
}
}
]
}
}
```
The button will be visible only when the linked rule evaluates to `true`.
## Application Evaluators
| Key | Description |
| ------------------------------- | ------------------------------------------------------------ |
| app.selection.canDelete | User has permission to delete selected node(s). |
| app.selection.canDownload | User can download selected node(s). |
| app.selection.notEmpty | At least one node is selected. |
| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. |
| app.selection.canAddFavorite | User can add selected node(s) to favorites. |
| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. |
| app.selection.first.canUpdate | User has permission to update selected node(s). |
| app.selection.file | A single File node is selected. |
| app.selection.file.canShare | User is able to share the selected file. |
| app.selection.file.isShared | A shared node is selected |
| app.selection.file.isLocked | File is locked for editing |
| app.selection.library | A single Library node is selected. |
| app.selection.isPrivateLibrary | A private Library node is selected. |
| app.selection.hasLibraryRole | The selected Library node has a role property. |
| app.selection.hasNoLibraryRole | The selected Library node has no role property. |
| app.selection.folder | A single Folder node is selected. |
| app.selection.folder.canUpdate | User has permissions to update the selected folder. |
| repository.isQuickShareEnabled | Whether the quick share repository option is enabled or not. |
## Navigation Evaluators
The application exposes a set of navigation-related evaluators to help developers restrict or enable certain actions based on the route or page displayed.
The negated evaluators are provided just to simplify development, and to avoid having complex rule trees just to negate the rules,
for example mixing `core.every` and `core.not`.
<p class="tip">
You can also negate any rule by utilizing a `!` prefix:
`!app.navigation.isTrashcan` is the opposite of the `app.navigation.isTrashcan`.
</p>
| Key | Description |
| --------------------------------- | ------------------------------------------------------- |
| app.navigation.folder.canCreate | User can create content in the currently opened folder. |
| app.navigation.folder.canUpload | User can upload content to the currently opened folder. |
| app.navigation.isTrashcan | User is using the **Trashcan** page. |
| app.navigation.isNotTrashcan | Current page is not a **Trashcan**. |
| app.navigation.isLibraries | User is using a **Libraries** page. |
| app.navigation.isNotLibraries | Current page is not a **Libraries** page. |
| app.navigation.isSharedFiles | User is using the **Shared Files** page. |
| app.navigation.isNotSharedFiles | Current page is not **Shared Files**. |
| app.navigation.isFavorites | User is using the **Favorites** page. |
| app.navigation.isNotFavorites | Current page is not **Favorites** |
| app.navigation.isRecentFiles | User is using the **Recent Files** page. |
| app.navigation.isNotRecentFiles | Current page is not **Recent Files**. |
| app.navigation.isSearchResults | User is using the **Search Results** page. |
| app.navigation.isNotSearchResults | Current page is not the **Search Results**. |
<p class="tip">
See [Registration](/extending/registration) section for more details
on how to register your own entries to be re-used at runtime.
</p>
### Example
The rule in the example below evaluates to `true` if all the conditions are met:
- user has selected node(s)
- user is not using the **Trashcan** page
- user is not using a **Libraries** page (**My Libraries**, **Favorite Libraries** or **Libraries Search Results** pages)
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"rules": [
{
"id": "app.toolbar.canCopyNode",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.notEmpty" },
{ "type": "rule", "value": "app.navigation.isNotTrashcan" },
{ "type": "rule", "value": "app.navigation.isNotLibraries" }
]
}
]
}
```
+276
View File
@@ -0,0 +1,276 @@
---
---
# Tutorials
## Custom route with parameters
In this tutorial, we are going to implement the following features:
- Update the **Trashcan** component to receive and log route parameters.
- Create a new route that points to the **Trashcan** component and uses the main layout.
- Create an action reference that allows redirecting to the new route.
- Create a button in the **New** menu that invokes an action.
Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body:
```typescript
import { ActivatedRoute, Params } from '@angular/router';
@Component({...})
export class TrashcanComponent {
constructor(
// ...
private route: ActivatedRoute
) {}
ngOnInit() {
// ...
this.route.params.subscribe(({ nodeId }: Params) => {
console.log('node: ', nodeId);
});
}
}
```
The code above logs the current route parameters to the browser console
and is proof the integration works as expected.
Next, add a new route definition as in the example below:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [
{
"id": "custom.routes.trashcan",
"path": "ext/trashcan/:nodeId",
"component": "your.component.id",
"layout": "app.layout.main",
"auth": ["app.auth"]
}
]
}
```
The template above creates a new route reference with the id `custom.routes.trashcan` that points to the `ext/trashcan/` route and accepts the `nodeId` parameter.
Also, we are going to use the default application layout (`app.layout.main`)
and authentication guards (`app.auth`).
Next, create an action reference for the `NAVIGATE_ROUTE` application action
and pass route parameters: `/ext/trashcan` for the path, and `10` for the `nodeId` value.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [...],
"actions": [
{
"id": "custom.actions.trashcan",
"type": "NAVIGATE_ROUTE",
"payload": "$(['/ext/trashcan', '10'])"
}
]
}
```
Finally, declare a new menu item for the `NEW` button and use the `custom.actions.trashcan` action created above.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [...],
"actions": [...],
"features": {
"create": [
{
"id": "custom.create.trashcan",
"type": "default",
"icon": "build",
"title": "Custom trashcan route",
"actions": {
"click": "custom.actions.trashcan"
}
}
]
}
}
```
Now, if you run the application, you should see a new menu item called "Custom Trashcan Route" in the "NEW" dropdown.
Upon clicking this item you should navigate to the `/ext/trashcan/10` route containing a **Trashcan** component.
Check the browser console output and ensure you have the following output:
```text
node: 10
```
You have successfully created a new menu button that invokes your custom action
and redirects you to the extra application route.
## Dialog actions
In this tutorial, we are going to create an action that invokes a custom material dialog.
Please read more details on Dialog components here: [Dialog Overview](https://material.angular.io/components/dialog/overview)
### Create a dialog
```sh
ng g component dialogs/my-extension-dialog --module=app
```
According to Angular rules, the component needs to also be registered within the `entryComponents` section of the module.
Update the `src/app/app.module.ts` file according to the example below:
```ts
@NgModule({
imports: [...],
declarations: [
...,
MyExtensionDialogComponent
],
entryComponents: [
...,
MyExtensionDialogComponent
]
})
```
Update `my-extension-dialog.component.ts`:
```ts
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'aca-my-extension-dialog',
templateUrl: './my-extension-dialog.component.html',
styleUrls: ['./my-extension-dialog.component.scss']
})
export class MyExtensionDialogComponent {
constructor(public dialogRef: MatDialogRef<MyExtensionDialogComponent>) {}
}
```
Update `my-extension-dialog.component.html`:
```html
<h2 mat-dialog-title>Delete all</h2>
<mat-dialog-content>Are you sure?</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close>No</button>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button mat-button [mat-dialog-close]="true">Yes</button>
</mat-dialog-actions>
```
### Create an action
Append the following code to the `src/app/store/actions/app.actions.ts`:
```ts
export const SHOW_MY_DIALOG = 'SHOW_MY_DIALOG';
export class ShowMydDialogAction implements Action {
readonly type = SHOW_MY_DIALOG;
}
```
See also:
- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
### Create an effect
Update `src/app/store/effects/app.effects.ts`:
```ts
import { ShowMydDialogAction, SHOW_MY_DIALOG } from '../actions/app.actions';
@Injectable()
export class AppEffects {
constructor(...) {}
@Effect({ dispatch: false })
showMyDialog$ = this.actions$.pipe(
ofType<ShowMydDialogAction>(SHOW_MY_DIALOG),
map(() => {})
);
// ...
}
```
See also:
- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
Update to raise a dialog
```ts
import { MatDialog } from '@angular/material';
import { MyExtensionDialogComponent } from '../../dialogs/my-extension-dialog/my-extension-dialog.component';
@Injectable()
export class AppEffects {
constructor(
...,
private dialog: MatDialog
) {}
@Effect({ dispatch: false })
showMyDialog$ = this.actions$.pipe(
ofType<ShowMydDialogAction>(SHOW_MY_DIALOG),
map(() => {
this.dialog.open(MyExtensionDialogComponent)
})
);
...
}
```
### Register a toolbar action
Update the `src/assets/app.extensions.json` file, and insert a new entry to the `features.toolbar` section:
```json
{
...,
"features": {
"toolbar": [
{
"id": "my.custom.toolbar.button",
"order": 10,
"title": "Custom action",
"icon": "extension",
"actions": {
"click": "SHOW_MY_DIALOG"
}
}
]
}
}
```
Now, once you run the application, you should see an extra button that invokes your dialog on every click.
+24
View File
@@ -0,0 +1,24 @@
---
---
# Application features
The concept of this application is a simple user interface which makes accessing files in the Alfresco Content Services repository easy.
Often Content Management systems provide more capabilities out of the box than most users need;
providing too many capabilities to these users prevents them from working efficiently,
so they may end up using unsanctioned file management solutions which presents a proliferation of content storage
and collaboration solutions as well as compliance issues for organizations.
This application simplifies the complexity of Content Management and provides comprehensive extensibility features for developers, using the Alfresco Application Development Framework, to easily and quickly create custom solutions for specific user cases.
## Contents
- [User interface layout](/features/user-interface-layout)
- [Header](/features/header)
- [Side navigation](/features/side-navigation)
- [Document List Layout](/features/document-list-layout)
- [File Viewer](/features/file-viewer)
- [Info Drawer](/features/info-drawer)
- [Version Manager](/features/version-manager)
- [Search results](/features/search-results)
+170
View File
@@ -0,0 +1,170 @@
---
---
# Document List Layout
The main area of the application is composed of several individual ADF components:
1. [Breadcrumb](https://alfresco.github.io/adf-component-catalog/components/BreadcrumbComponent.html)
2. [Toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html)
3. [Document List](https://alfresco.github.io/adf-component-catalog/components/DocumentListComponent.html)
4. [Pagination](https://alfresco.github.io/adf-component-catalog/components/PaginationComponent.html)
![](../images/doclist.png)
The application has seven different Document List views which share commonalities between each view and subtle differences depending on the content being loaded which are explained below.
## Personal Files
Personal Files retrieves all content from the logged in user's home area (`/User Homes/<username>/`) in the repository;
if the user is admin who does not have a home folder then the repository root folder is shown.
Personal Files is the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component,
using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes).
## File Libraries
File Libraries retrieves all the sites that the user is a member of including what type of site it is: public, moderated or private.
File Libraries is the [Libraries](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/libraries) component,
using the [Sites API](https://api-explorer.alfresco.com/api-explorer/#/sites).
When a user opens one of their sites then the content for the site's document library is shown.
To display the files and folders from a site (`/Sites/<siteid>/Document Library/`) the [Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/files) component,
using the [Nodes API](https://api-explorer.alfresco.com/api-explorer/#/nodes) is used.
## Shared Files
The Shared Files view aggregates all files that have been shared using the QuickShare feature in the content repository.
The [Shared Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/shared-files) component uses the [shared-links API](https://api-explorer.alfresco.com/api-explorer/#/shared-links)
and includes extra columns to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository and who created the shared link.
## Recent Files
The Recent Files view shows all the files that have been created or modified within the last 30 days by the current user.
The [Recent Files](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/recent-files)
component uses the Search API to query SOLR for changes made by the user and includes an extra column to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository.
## Favorites
The Favorites view shows all files and folders from the content repository that have been marked as a favorite by the current user.
The [Favorites](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/favorites) component uses the
[favorites](https://api-explorer.alfresco.com/api-explorer/#/favorites) API to retrieve all the favorite nodes for the user
and includes an extra column to display where the file is
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository.
## Trash
The Trash view shows all the items that a user has deleted, admin will see items deleted by all users.
The actions available in this view are Restore and Permanently Delete.
The [Trashcan](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/trashcan) component uses the
[trashcan](https://api-explorer.alfresco.com/api-explorer/#/trashcan) API to retrieve the deleted items
and perform the actions requested by the user and includes an extra column to display where the item was
[located](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/location-link)
in the content repository before it was deleted.
## Search Results
The Search Results view shows the found items for a search query. It has a custom layout template and users can easily browse the results and perform actions on items.
For more information on the [SearchComponent](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search), please also check this [Search Results](/features/search-results) section.
Another custom template layout shows the results when searching for libaries so that users can find, join and favorite libraries that they aren't already members of.
## Actions and the Actions Toolbar
All the views incorporate the [toolbar](https://alfresco.github.io/adf-component-catalog/components/ToolbarComponent.html)
component from the Alfresco Application Development Framework.
Actions are displayed in the toolbar when item(s) are selected, or a right click is performed; apart from the Trash view they all display the following actions when the current user has the necessary permissions,
actions are automatically hidden when the user does not have permission.
<table>
<thead>
<th>Action</th>
<th>File</th>
<th>Folder</th>
</thead>
<tbody>
<tr>
<td>View</td>
<td>
Opens the selected file using the <a href="https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/preview" target="_blank">Preview</a> component,
where the file cannot be displayed natively in a browser a PDF rendition is obtained from the repository.
</td>
<td>Not applicable</td>
</tr>
<tr>
<td>Download</td>
<td>Downloads single files to the user's computer, when multiple files are selected they are compressed into a ZIP and then downloaded.</td>
<td>Folders are automatically compressed into a ZIP and then downloaded to the user's computer.</td>
</tr>
<tr>
<td>Edit</td>
<td>Not applicable</td>
<td>The folder name and description can be edited in a dialog.</td>
</tr>
<tr>
<td>Favorite</td>
<td colspan="2">
Toggle the favorite mark on or off for files and folders, when multiple items are selected
and one or more are not favorites then the mark will be toggled on.
</td>
</tr>
<tr>
<td>Copy</td>
<td colspan="2">
Files and folders can be copied to another location in the content repository using the
<a href="https://alfresco.github.io/adf-component-catalog/components/ContentNodeSelectorComponent.html" target="_blank">content-node-selector</a> component;
once the copy action has completed the user is notified and can undo the action (which permanently deletes the created copies).
</td>
</tr>
<tr>
<td>Move</td>
<td colspan="2">
Files and folders can be moved to another location in the content repository using the
<a href="https://alfresco.github.io/adf-component-catalog/components/ContentNodeSelectorComponent.html" target="_blank">content-node-selector</a> component;
once the move action has completed the user is notified and can undo the action (which moves the items back to the original location).
</td>
</tr>
<tr>
<td>Share</td>
<td>
Create and copy a link to a file that can be shared, the links are accessible without granting permissions to the file, and do not require users to login to the application. Share links can automatically expire based on a date, the minimum expiry date is controlled by the Content Services repository, which is 1 day from the date of creation.
</td>
<td>
Not applicable.
</td>
</tr>
<tr>
<td>Delete</td>
<td colspan="2">
Files and folders can be deleted from their location in the content repository;
once the delete action has completed the user is notified and can undo the action (which restores the items from the trash).
</td>
</tr>
<tr>
<td>Manage Versions</td>
<td>
Versions of files can be viewed, uploaded, restored, downloaded and deleted by using the version manager dialog;
once each action has completed the list of versions is updated according to the change.
</td>
<td>Not applicable</td>
</tr>
<tr>
<td>Permissions</td>
<td>
Permissions on a file can be adjusted as required in a number of ways; disable inheritance from the parent folder, change a user or groups role and grant users/groups access.
</td>
<td>Not available</td>
</tr>
</tbody>
</table>
Besides the actions available in the toolbar users can
* single click on a file to view it, and a folder to open it
* single click an item to select it
* double click on a file to view it, and a folder to open it
+58
View File
@@ -0,0 +1,58 @@
---
---
# File Viewer
The File Viewer has been created using the [ViewerComponent](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html) from the ADF. The Viewer has four main areas:
![File Viewer](../images/File-Viewer.png)
1. [Header & Toolbar](#header-and-toolbar)
2. [Content](#content)
3. [Thumbnails side pane](#thumbnails-side-pane)
4. [Viewer Controls](#viewer-controls)
## Header and Toolbar
The Header & Toolbar section of the viewer contains a number of features that relate to the file currently being displayed:
- Close 'X' will return the user to the folder that contains the file.
- The name and file type icon is shown in the middle.
- Next and previous buttons will be displayed either side of the file name so that users can navigate to other files in the folder without navigating away from the viewer.
- Finally, on the right hand side an actions toolbar provides users with the ability to download, favorite, move, copy, delete, manage versions and view info panel.
## Content
The File Viewer consists of four separate views that handle displaying the content based on four types of content, covering various [file/mime](https://alfresco.github.io/adf-component-catalog/components/ViewerComponent.html#supported-file-formats) types:
- Document View: PDF files are displayed in the application File Viewer, for other document types (DOCX etc) then a PDF rendition is automatically retrieved.
- Image View: JPEG, PNG, GIF, BMP and SVG images are natively displayed in the application File Viewer.
- Media View: MP4, MP3, WAV, OGG and WEBM files are played natively application File Viewer. The File Viewer will download, by default, 50MB of the content at a time to ensure a smooth playback of the content.
- Text View: TXT, XML, JS, HTML, JSON and TS files are natively displayed as text in the application File Viewer.
## Thumbnails side pane
The Document View includes a thumbnails pane which can be activated by a button in the Viewer Actions toolbar. Clicking on a thumbnail will take a user directly to the selected page and as users scroll through a document the current page is highlighted in the pane.
## Viewer Controls
At the bottom of the content the Viewer Controls allow users to interact with the content in various ways; the actions available are dependant on the type of content being displayed.
- Document View:
- Activate/Deactivate thumbnails pane
- Previous/Next page
- Jump to page number
- Zoom in/out
- Fit to page
- Print
- Image View:
- Zoom in/out
- Rotate left/right (does not alter content in the repository)
- Reset image
- Print
- Media View:
- Play/pause
- Timeline position
- Toggle audio
- Audio volume
- Full screen
+39
View File
@@ -0,0 +1,39 @@
---
---
# Header
The application [header](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/header) has three main elements.
1. [Logo and Color](#logo-and-color)
2. [Search](#search)
3. [Current User](#current-user)
![Header](../images/header.png)
## Logo and Color
Logo & app primary color - logo and color are configurable by updating the
[app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json) file in the root folder of the project.
Please refer to the [Application Configuration](/getting-started/configuration) documentation for more information on how to change the logo and color.
## Search
The application [Search](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/search) -
uses the [ADF Search Component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/search).
The app provides a 'live' search feature, where users can open files and folders directly from the Search API results.
![Search Input](../images/search.png)
If you type `Enter` in the text input area, you are going to see
[Search Results](/features/search-results) page
with advanced filtering and faceted search.
## Current User
[Current User](https://github.com/Alfresco/alfresco-content-app/tree/development/src/app/components/current-user) -
displays the user's name, and a menu where users can logout.
Optionally through updating the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json)
a language switching menu can be displayed.
![Current User](../images/current-user.png)
+20
View File
@@ -0,0 +1,20 @@
---
---
# Info Drawer
The Info Drawer displays node information in the right sidebar panel. It is created by using the [InfoDrawerComponent](https://alfresco.github.io/adf-component-catalog/components/InfoDrawerComponent.html). This info is available for both folder and file nodes.
Currently, there are 2 tabs available: Properties and Versions.
## Properties tab
The Properties tab displays the node's metadata info by using the [ContentMetadataCardComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataCardComponent.html).
![](images/content-metadata.png)
For more information, please check also the [ContentMetadataComponent](https://alfresco.github.io/adf-component-catalog/components/ContentMetadataComponent.html).
## Comments tab
The Comments tab displays all comments made on the selected node in the respoistory by using the [CommentsComponent](https://alfresco.github.io/adf-component-catalog/components/CommentsComponent.html). Users can post new comments that will be displayed immediately.
+19
View File
@@ -0,0 +1,19 @@
---
---
# Search Results
Once you type the text in the Search Input component you are going to see the Search Results page
![Search Results](../images/aca-search-results.png)
This page consists of the following ADF components:
- [Search Filter](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-filter.component.md)
- [Search Chip List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-chip-list.component.md)
- [Search Sorting Picker](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/search-sorting-picker.component.md)
- [Document List](https://github.com/Alfresco/alfresco-ng2-components/blob/master/docs/content-services/document-list.component.md) with custom layout template
- [Info Drawer](/features/info-drawer) with Metadata and [Version Management](#version-manager)
- [Toolbar with basic actions](/features/document-list-layout#actions-and-the-actions-toolbar) like `Preview`, `Download`, `Favorite`, `Copy`, etc.
And also the Info Drawer, Toolbar and Node Selector dialogs for copy and move operations.
+30
View File
@@ -0,0 +1,30 @@
---
---
# Side Navigation
The application [side navigation](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/sidenav) has two features:
a button menu and navigation links.
![Side Navigation](../images/side-nav.png)
## New button
The New button displays a menu which provides three actions:
- Create a new folder - provides a dialog which allows the creation of a new folder, the folder name is mandatory and the description is optional.
- Upload a file - invokes the operating system file browser and allows a user to select file(s) to upload into their current location in the content repository.
- Upload a folder - invokes the operating system folder browser and allows a user to select a folder to upload to their current location in the content repository.
When an upload starts the [upload component](https://github.com/Alfresco/alfresco-ng2-components/tree/master/lib/content-services/upload)
is displayed which shows the user the progress of the uploads they have started.
The upload dialog persists on the screen and can be minimized; users are able to continue using the application whilst uploads are in progress
and uploads can be canceled which will stop uploads in progress or permanently delete already completed uploads.
![Uploader](../images/uploader.png)
## Navigation
The navigation links are configurable via the [app.config.json](https://github.com/Alfresco/alfresco-content-app/blob/master/src/app.config.json).
Default configuration creates two sections.
See [Navigation](/getting-started/navigation) for more information about configuring the side navigation.
+12
View File
@@ -0,0 +1,12 @@
---
---
# User Interface - layout
There are three main areas of the application controlled by the [Layout component](https://github.com/Alfresco/alfresco-content-app/tree/master/src/app/components/layout):
1. [Application Header](/features/header)
2. [Side Navigation](/features/side-navigation)
3. [Document List](/features/document-list-layout)
![Features](../images/features-01.png)
+42
View File
@@ -0,0 +1,42 @@
---
---
# Version Manager
The versions of a file can be viewed and managed by using the [VersionManagerComponent](https://alfresco.github.io/adf-component-catalog/components/VersionManagerComponent.html).
There are 2 ways users can access the Version Manager:
1) From the 'Manage Versions' option of the 'More actions' menu (check [Actions and the Actions Toolbar](/features/document-list-layout#actions-and-the-actions-toolbar)):
![Version Manager Menu](../images/version-manager-action.png)
![Version Manager Dialog](../images/version-manager-dialog.png)
2) From the [Info Drawer](/features/info-drawer) (the Details right panel):
![Version Manager Inline](../images/version-manager-tab.png)
## Upload new version
A new version for the selected file can be added by using this button. Users can upload a new file version using a file that is does not have the same name, or mime type as the current version, whilst allowing the user to choose the type of version (minor or major) and inputting supporting comments.
Please also check the [UploadVersionButtonComponent](https://alfresco.github.io/adf-component-catalog/components/UploadVersionButtonComponent.html).
## Actions Menu
Each item in the version list has a couple of actions available: Restore, Download and Delete. These are displayed if user has permission to do that specific action. The 'Download' and 'Delete' can be also disabled from the app.config.
In the app.config.json file, these are the current settings for the ACA version manager:
```json
{
"adf-version-manager": {
"allowComments": true,
"allowDownload": true
}
}
```
Set the allowComments to false if the version comments should not be displayed on the version list.
Clicking to delete a version of a file triggers a confirmation dialog. Please see the [ConfirmDialogComponent](https://alfresco.github.io/adf-component-catalog/components/ConfirmDialogComponent.html) for more info.
+1 -1
View File
@@ -163,4 +163,4 @@ Map the `/custom-route` in `app.routes.ts` as a child of `LayoutComponent` defin
![](../images/navigation-03.png)
For more information about the content of a custom page see [Document List Layout](/#document-list-layout) section.
For more information about the content of a custom page see [Document List Layout](/features/document-list-layout) section.
-6
View File
@@ -12,12 +12,6 @@ Visit Alfresco Community for resources that will help you find information to ge
along with blog posts from the Alfresco developers and much more:
https://community.alfresco.com/community/application-development-framework
## Alfresco ADF Gitter
Join the vibrant community in Gitter where you can chat with experienced developers,
including the Alfresco employees working directly on the ADF and this example application project:
https://gitter.im/Alfresco/alfresco-ng2-components
## Alfresco Developer Support
Developer Support is a remote subscription-based support team that offers
+9 -1
View File
@@ -20,13 +20,21 @@
title: 'Home',
path: '/'
},
{
title: 'App features',
path: '/features/'
},
{
title: 'Getting Started',
path: '/getting-started/'
},
{
title: 'Extending',
path: '/extending'
path: '/extending/'
},
{
title: 'Tutorials',
path: '/tutorials/'
},
{
title: 'Get Help',
+36 -43
View File
@@ -1,46 +1,39 @@
- [Home](/)
- [Introduction](/#introduction)
- [Features](/#features)
- [User interface layout](/#user-interface-layout)
- [Header](/#header)
- [Side navigation](/#side-navigation)
- [Document List Layout](/#document-list-layout)
- [File Viewer](/#file-viewer)
- [Info Drawer](/#info-drawer)
- [Version Manager](/#version-manager)
- [Search results](/#search-results1)
- [How to contribute](/#how-to-contribute)
- [Documentation](/#documentation)
- [How to contribute](/#how-to-contribute)
- [App features](/features/)
- [User interface layout](/features/user-interface-layout)
- [Header](/features/header)
- [Side navigation](/features/side-navigation)
- [Document List Layout](/features/document-list-layout)
- [File Viewer](/features/file-viewer)
- [Info Drawer](/features/info-drawer)
- [Version Manager](/features/version-manager)
- [Search results](/features/search-results)
- [Getting started](/getting-started/)
- [Prerequisites](/getting-started/prerequisites)
- [Building from source](/getting-started/building-from-source)
- [Internationalization (i18n)](/getting-started/internationalization)
- [CORS](/getting-started/cors)
- [Configuration](/getting-started/configuration)
- [Navigation](/getting-started/navigation)
- [Docker](/getting-started/docker)
- [Extending](/extending)
- [Format](/extending#format)
- [Schema](/extending#schema)
- [Multiple files](/extending#multiple-files)
- [Startup behaviour](/extending#startup-behaviour)
- [Disabling content](/extending#disabling-content)
- [Routes](/extending#routes)
- [Components](/extending#components)
- [Actions](/extending#actions)
- [Application actions](/extending#application-actions)
- [Rules](/extending#rules)
- [Core evaluators](/extending#core-evaluators)
- [Application evaluators](/extending#application-evaluators)
- [Navigation evaluators](/extending#navigation-evaluators)
- [Application features](/extending#application-features)
- [Content actions](/extending#content-actions)
- [Create menu](/extending#create-menu)
- [Navigation bar](/extending#navigation-bar)
- [Sidebar (Info Drawer)](/extending#sidebar-info-drawer)
- [Toolbar](/extending#toolbar)
- [Context menu](/extending#context-menu)
- [Viewer](/extending#viewer)
- [Registration](/extending#registration)
- [Creating custom evaluator](/extending#creating-custom-evaluator)
- [Tutorials](/extending#tutorials)
- [Prerequisites](/getting-started/prerequisites)
- [Building from source](/getting-started/building-from-source)
- [Internationalization (i18n)](/getting-started/internationalization)
- [CORS](/getting-started/cors)
- [Configuration](/getting-started/configuration)
- [Navigation](/getting-started/navigation)
- [Docker](/getting-started/docker)
- [Extending](/extending/)
- [Extensibility features](/extending/extensibility-features)
- [Extension format](/extending/extension-format)
- [Routes](/extending/routes)
- [Components](/extending/components)
- [Actions](/extending/actions)
- [Application actions](/extending/application-actions)
- [Rules](/extending/rules)
- [Application features](/extending/application-features)
- [Custom icons](/extending/icons)
- [Registration](/extending/registration)
- [Creating custom evaluators](/extending/creating-custom-evaluators)
- [Tutorials](/extending/tutorials)
- [Redistributable libraries](/extending/redistributable-libraries)
- [Tutorials](/tutorials/)
- [Introduction to extending ACA](/tutorials/introduction-to-extending)
- [Custom route with parameters](/tutorials/custom-route-with-parameters)
- [Dialog actions](/tutorials/dialog-actions)
- [Get help](/help)
+12
View File
@@ -0,0 +1,12 @@
---
---
# Tutorials
Learn more about developing with the Alfresco Content Application.
## Contents
- [Introduction to extending ACA](/tutorials/introduction-to-extending)
- [Custom route with parameters](/tutorials/custom-route-with-parameters)
- [Dialog actions](/tutorials/dialog-actions)
@@ -0,0 +1,123 @@
---
---
# Custom route with parameters
In this tutorial, we are going to implement the following features:
- Update the **Trashcan** component to receive and log route parameters.
- Create a new route that points to the **Trashcan** component and uses the main layout.
- Create an action reference that allows redirecting to the new route.
- Create a button in the **New** menu that invokes an action.
Update `src/app/components/trashcan/trashcan.component.ts` and append the following code to the `ngOnInit` body:
```typescript
import { ActivatedRoute, Params } from '@angular/router';
@Component({...})
export class TrashcanComponent {
constructor(
// ...
private route: ActivatedRoute
) {}
ngOnInit() {
// ...
this.route.params.subscribe(({ nodeId }: Params) => {
console.log('node: ', nodeId);
});
}
}
```
The code above logs the current route parameters to the browser console
and is proof the integration works as expected.
Next, add a new route definition as in the example below:
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [
{
"id": "custom.routes.trashcan",
"path": "ext/trashcan/:nodeId",
"component": "your.component.id",
"layout": "app.layout.main",
"auth": ["app.auth"]
}
]
}
```
The template above creates a new route reference with the id `custom.routes.trashcan` that points to the `ext/trashcan/` route and accepts the `nodeId` parameter.
Also, we are going to use the default application layout (`app.layout.main`)
and authentication guards (`app.auth`).
Next, create an action reference for the `NAVIGATE_ROUTE` application action
and pass route parameters: `/ext/trashcan` for the path, and `10` for the `nodeId` value.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [...],
"actions": [
{
"id": "custom.actions.trashcan",
"type": "NAVIGATE_ROUTE",
"payload": "$(['/ext/trashcan', '10'])"
}
]
}
```
Finally, declare a new menu item for the `NEW` button and use the `custom.actions.trashcan` action created above.
```json
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "plugin1",
"routes": [...],
"actions": [...],
"features": {
"create": [
{
"id": "custom.create.trashcan",
"type": "default",
"icon": "build",
"title": "Custom trashcan route",
"actions": {
"click": "custom.actions.trashcan"
}
}
]
}
}
```
Now, if you run the application, you should see a new menu item called "Custom Trashcan Route" in the "NEW" dropdown.
Upon clicking this item you should navigate to the `/ext/trashcan/10` route containing a **Trashcan** component.
Check the browser console output and ensure you have the following output:
```text
node: 10
```
You have successfully created a new menu button that invokes your custom action
and redirects you to the extra application route.
+153
View File
@@ -0,0 +1,153 @@
---
---
# Dialog actions
In this tutorial, we are going to create an action that invokes a custom material dialog.
Please read more details on Dialog components here: [Dialog Overview](https://material.angular.io/components/dialog/overview)
## Create a dialog
```sh
ng g component dialogs/my-extension-dialog --module=app
```
According to Angular rules, the component needs to also be registered within the `entryComponents` section of the module.
Update the `src/app/app.module.ts` file according to the example below:
```ts
@NgModule({
imports: [...],
declarations: [
...,
MyExtensionDialogComponent
],
entryComponents: [
...,
MyExtensionDialogComponent
]
})
```
Update `my-extension-dialog.component.ts`:
```ts
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material';
@Component({
selector: 'aca-my-extension-dialog',
templateUrl: './my-extension-dialog.component.html',
styleUrls: ['./my-extension-dialog.component.scss']
})
export class MyExtensionDialogComponent {
constructor(public dialogRef: MatDialogRef<MyExtensionDialogComponent>) {}
}
```
Update `my-extension-dialog.component.html`:
```html
<h2 mat-dialog-title>Delete all</h2>
<mat-dialog-content>Are you sure?</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close>No</button>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button mat-button [mat-dialog-close]="true">Yes</button>
</mat-dialog-actions>
```
## Create an action
Append the following code to the `src/app/store/actions/app.actions.ts`:
```ts
export const SHOW_MY_DIALOG = 'SHOW_MY_DIALOG';
export class ShowMydDialogAction implements Action {
readonly type = SHOW_MY_DIALOG;
}
```
See also:
- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
## Create an effect
Update `src/app/store/effects/app.effects.ts`:
```ts
import { ShowMydDialogAction, SHOW_MY_DIALOG } from '../actions/app.actions';
@Injectable()
export class AppEffects {
constructor(...) {}
@Effect({ dispatch: false })
showMyDialog$ = this.actions$.pipe(
ofType<ShowMydDialogAction>(SHOW_MY_DIALOG),
map(() => {})
);
// ...
}
```
See also:
- [Comprehensive Introduction to @ngrx/store](https://gist.github.com/btroncone/a6e4347326749f938510)
Update to raise a dialog
```ts
import { MatDialog } from '@angular/material';
import { MyExtensionDialogComponent } from '../../dialogs/my-extension-dialog/my-extension-dialog.component';
@Injectable()
export class AppEffects {
constructor(
...,
private dialog: MatDialog
) {}
@Effect({ dispatch: false })
showMyDialog$ = this.actions$.pipe(
ofType<ShowMydDialogAction>(SHOW_MY_DIALOG),
map(() => {
this.dialog.open(MyExtensionDialogComponent)
})
);
...
}
```
## Register a toolbar action
Update the `src/assets/app.extensions.json` file, and insert a new entry to the `features.toolbar` section:
```json
{
...,
"features": {
"toolbar": [
{
"id": "my.custom.toolbar.button",
"order": 10,
"title": "Custom action",
"icon": "extension",
"actions": {
"click": "SHOW_MY_DIALOG"
}
}
]
}
}
```
Now, once you run the application, you should see an extra button that invokes your dialog on every click.
@@ -0,0 +1,6 @@
---
---
# Introduction to extending the Alfresco Content Application
An introductory tutorial will appear here shortly.
+1 -1
View File
@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, ElementArrayFinder, by, promise } from 'protractor';
import { ElementFinder, ElementArrayFinder, by } from 'protractor';
import { Component } from '../component';
export class Breadcrumb extends Component {
+10 -6
View File
@@ -46,6 +46,7 @@ export class DataTable extends Component {
cell: '.adf-data-table-cell',
locationLink: '.aca-location-link',
nameLink: '.dl-link',
libraryRole: 'app-library-role-column',
selectedIcon: '.mat-icon',
@@ -76,11 +77,11 @@ export class DataTable extends Component {
// Wait methods (waits for elements)
waitForHeader() {
try {
return browser.wait(EC.presenceOf(this.head), BROWSER_WAIT_TIMEOUT);
} catch (error) {
console.log('----- wait for header catch : ', error);
}
return browser.wait(EC.presenceOf(this.head), BROWSER_WAIT_TIMEOUT, '--- timeout waitForHeader ---');
}
waitForBody() {
return browser.wait(EC.presenceOf(this.body), BROWSER_WAIT_TIMEOUT, '--- timeout waitForBody ---');
}
async waitForEmptyState() {
@@ -199,7 +200,6 @@ export class DataTable extends Component {
async selectItem(name: string) {
try{
const item = this.getRowFirstCell(name);
// await Utils.waitUntilElementClickable(item);
await item.click();
} catch (e) {
@@ -315,4 +315,8 @@ export class DataTable extends Component {
const count = await this.menu.getItemsCount();
return count > 0;
}
async getLibraryRole(name: string) {
return await this.getRowByName(name).element(by.css(DataTable.selectors.libraryRole)).getText();
}
}
@@ -26,6 +26,7 @@
import { ElementFinder, by, browser, ExpectedConditions as EC } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Component } from '../component';
import * as moment from 'moment';
export class DateTimePicker extends Component {
private static selectors = {
@@ -72,11 +73,12 @@ export class DateTimePicker extends Component {
}
async setDefaultDay() {
const today = await this.dayPicker.element(by.css(DateTimePicker.selectors.today)).getText();
const tomorrow = (parseInt(today, 10) + 1).toString();
const today = moment();
const tomorrow = today.add(1, 'day');
const dayOfTomorrow = tomorrow.date();
const date = await this.getDate();
const year = await this.getYear();
const elem = this.dayPicker.element(by.cssContainingText(DateTimePicker.selectors.firstActiveDay, tomorrow));
const elem = this.dayPicker.element(by.cssContainingText(DateTimePicker.selectors.firstActiveDay, `${dayOfTomorrow}`));
await elem.click();
return `${date} ${year}`;
}
@@ -57,7 +57,11 @@ export class CreateOrEditFolderDialog extends Component {
}
async waitForDialogToClose() {
await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT);
await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT, '---- timeout waiting for dialog to close ----');
}
async isDialogOpen() {
return await browser.isElementPresent(by.css(CreateOrEditFolderDialog.selectors.root));
}
async getTitle() {
+173
View File
@@ -0,0 +1,173 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, by, browser, protractor, ExpectedConditions as EC } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Component } from '../component';
import { Utils } from '../../utilities/utils';
export class CreateLibraryDialog extends Component {
private static selectors = {
root: 'app-library-dialog',
title: '.mat-dialog-title',
nameInput: 'input[placeholder="Name" i]',
libraryIdInput: 'input[placeholder="Library ID" i]',
descriptionTextArea: 'textarea[placeholder="Description" i]',
button: '.mat-dialog-actions button',
radioButton: '.mat-radio-label',
radioChecked: 'mat-radio-checked',
errorMessage: '.mat-error'
};
title: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.title));
nameInput: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.nameInput));
libraryIdInput: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.libraryIdInput));
descriptionTextArea: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.descriptionTextArea));
visibilityPublic: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.radioButton, 'Public'));
visibilityModerated: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.radioButton, 'Moderated'));
visibilityPrivate: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.radioButton, 'Private'));
createButton: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.button, 'Create'));
cancelButton: ElementFinder = this.component.element(by.cssContainingText(CreateLibraryDialog.selectors.button, 'Cancel'));
errorMessage: ElementFinder = this.component.element(by.css(CreateLibraryDialog.selectors.errorMessage));
constructor(ancestor?: ElementFinder) {
super(CreateLibraryDialog.selectors.root, ancestor);
}
async waitForDialogToOpen() {
await browser.wait(EC.presenceOf(this.title), BROWSER_WAIT_TIMEOUT);
await browser.wait(EC.presenceOf(browser.element(by.css('.cdk-overlay-backdrop'))), BROWSER_WAIT_TIMEOUT);
}
async waitForDialogToClose() {
await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT);
}
async isDialogOpen() {
return await browser.isElementPresent(by.css(CreateLibraryDialog.selectors.root));
}
async getTitle() {
return await this.title.getText();
}
async isErrorMessageDisplayed() {
return await this.errorMessage.isDisplayed();
}
async getErrorMessage() {
await this.isErrorMessageDisplayed();
return await this.errorMessage.getText();
}
async isNameDisplayed() {
return await this.nameInput.isDisplayed();
}
async isLibraryIdDisplayed() {
return await this.libraryIdInput.isDisplayed();
}
async isDescriptionDisplayed() {
return await this.descriptionTextArea.isDisplayed();
}
async isPublicDisplayed() {
return await this.visibilityPublic.isDisplayed();
}
async isModeratedDisplayed() {
return await this.visibilityModerated.isDisplayed();
}
async isPrivateDisplayed() {
return await this.visibilityPrivate.isDisplayed();
}
async enterName(name: string) {
await this.nameInput.clear();
await Utils.typeInField(this.nameInput, name);
}
async enterLibraryId(id: string) {
await this.libraryIdInput.clear();
await Utils.typeInField(this.libraryIdInput, id);
}
async enterDescription(description: string) {
await this.descriptionTextArea.clear();
await Utils.typeInField(this.descriptionTextArea, description);
}
async deleteNameWithBackspace() {
await this.nameInput.clear();
await this.nameInput.sendKeys(' ', protractor.Key.CONTROL, 'a', protractor.Key.NULL, protractor.Key.BACK_SPACE);
}
async isCreateEnabled() {
return await this.createButton.isEnabled();
}
async isCancelEnabled() {
return await this.cancelButton.isEnabled();
}
async clickCreate() {
await this.createButton.click();
}
async clickCancel() {
await this.cancelButton.click();
await this.waitForDialogToClose();
}
async isPublicChecked() {
const elemClass = await this.visibilityPublic.element(by.xpath('..')).getAttribute('class');
return await elemClass.includes(CreateLibraryDialog.selectors.radioChecked);
}
async isModeratedChecked() {
const elemClass = await this.visibilityModerated.element(by.xpath('..')).getAttribute('class');
return await elemClass.includes(CreateLibraryDialog.selectors.radioChecked);
}
async isPrivateChecked() {
const elemClass = await this.visibilityPrivate.element(by.xpath('..')).getAttribute('class');
return await elemClass.includes(CreateLibraryDialog.selectors.radioChecked);
}
async selectPublic() {
await this.visibilityPublic.click();
}
async selectModerated() {
await this.visibilityModerated.click();
}
async selectPrivate() {
await this.visibilityPrivate.click();
}
}
+2 -20
View File
@@ -26,48 +26,30 @@
import { ElementFinder, by, browser } from 'protractor';
import { Component } from '../component';
import { UserInfo } from './user-info';
import { protractor } from 'protractor';
import { Utils } from '../../utilities/utils';
import { Menu } from '../menu/menu';
import { Toolbar } from './../toolbar/toolbar';
import { SearchInput } from '../search/search-input';
export class Header extends Component {
private locators = {
root: 'app-header',
logoLink: by.css('.app-menu__title'),
userInfo: by.css('aca-current-user'),
searchButton: by.css('#adf-search-button'),
searchBar: by.css('#adf-control-input'),
moreActions: by.id('app.header.more')
};
logoLink: ElementFinder = this.component.element(this.locators.logoLink);
userInfo: UserInfo = new UserInfo(this.component);
searchButton: ElementFinder = this.component.element(this.locators.searchButton);
searchBar: ElementFinder = this.component.element(this.locators.searchBar);
moreActions: ElementFinder = browser.element(this.locators.moreActions);
menu: Menu = new Menu();
toolbar: Toolbar = new Toolbar();
searchInput: SearchInput = new SearchInput();
constructor(ancestor?: ElementFinder) {
super('adf-layout-header', ancestor);
}
async searchForText(text: string) {
await this.searchBar.clear();
await this.searchBar.sendKeys(text);
await this.searchBar.sendKeys(protractor.Key.ENTER);
}
async waitForSearchButton() {
await Utils.waitUntilElementClickable(this.searchButton);
}
async waitForSearchBar() {
await Utils.waitUntilElementClickable(this.searchBar);
}
async openMoreMenu() {
await this.moreActions.click();
await this.menu.waitForMenuToOpen();
+208 -1
View File
@@ -26,6 +26,7 @@
import { ElementFinder, ElementArrayFinder, by, browser, ExpectedConditions as EC } from 'protractor';
import { Component } from '../component';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Utils } from './../../utilities/utils';
export class InfoDrawer extends Component {
private static selectors = {
@@ -40,10 +41,25 @@ export class InfoDrawer extends Component {
activeTabContent: '.mat-tab-body-active .mat-tab-body-content adf-dynamic-tab',
next: '.mat-tab-header-pagination-after .mat-tab-header-pagination-chevron',
previous: '.mat-tab-header-pagination-before .mat-tab-header-pagination-chevron'
previous: '.mat-tab-header-pagination-before .mat-tab-header-pagination-chevron',
headerTitle: '.adf-info-drawer-layout-header-title',
// metadata card
metadataTabContent: '.app-metadata-tab .mat-card-content',
metadataTabAction: '.app-metadata-tab .mat-card-actions .mat-button',
field: '.mat-form-field',
fieldLabelWrapper: '.mat-form-field-label-wrapper',
fieldInput: '.mat-input-element',
dropDown: '.mat-select',
visibilityOption: '.mat-option .mat-option-text',
hint: '.mat-hint',
error: '.mat-error'
};
header: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.header));
headerTitle: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.headerTitle));
tabLabel: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.tabLabel));
tabLabelsList: ElementArrayFinder = this.component.all(by.css(InfoDrawer.selectors.tabLabel));
tabActiveLabel: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.tabActiveLabel));
@@ -53,6 +69,19 @@ export class InfoDrawer extends Component {
nextButton: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.next));
previousButton: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.previous));
metadataTabContent: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.metadataTabContent));
metadataTabAction: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.metadataTabAction));
fieldLabelWrapper: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.fieldLabelWrapper));
fieldInput: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.fieldInput));
visibilityDropDown: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.dropDown));
visibilityPublic: ElementFinder = browser.element(by.cssContainingText(InfoDrawer.selectors.visibilityOption, 'Public'));
visibilityPrivate: ElementFinder = browser.element(by.cssContainingText(InfoDrawer.selectors.visibilityOption, 'Private'));
visibilityModerated: ElementFinder = browser.element(by.cssContainingText(InfoDrawer.selectors.visibilityOption, 'Moderated'));
hint: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.hint));
error: ElementFinder = this.component.element(by.css(InfoDrawer.selectors.error));
constructor(ancestor?: ElementFinder) {
super(InfoDrawer.selectors.root, ancestor);
}
@@ -61,6 +90,10 @@ export class InfoDrawer extends Component {
return await browser.wait(EC.presenceOf(this.header), BROWSER_WAIT_TIMEOUT);
}
async isOpen() {
return await browser.isElementPresent(this.header);
}
async isEmpty() {
if (await browser.isElementPresent(by.css(InfoDrawer.selectors.tabs))) {
return false;
@@ -91,5 +124,179 @@ export class InfoDrawer extends Component {
async getComponentIdOfTab() {
return await this.tabActiveContent.getAttribute('data-automation-id');
}
async getHeaderTitle() {
return await this.headerTitle.getText();
}
getLabelWrapper(label: string) {
return this.component.element(by.cssContainingText(InfoDrawer.selectors.fieldLabelWrapper, label));
}
getFieldByName(fieldName: string) {
const wrapper = this.getLabelWrapper(fieldName);
return wrapper.element(by.xpath('..')).element(by.css(InfoDrawer.selectors.fieldInput));
}
async isFieldDisplayed(fieldName: string) {
return await browser.isElementPresent(this.getFieldByName(fieldName));
}
async isInputEnabled(fieldName: string) {
return this.getFieldByName(fieldName).isEnabled();
}
async isVisibilityEnabled() {
const wrapper = this.getLabelWrapper('Visibility');
const field = wrapper.element(by.xpath('..')).element(by.css(InfoDrawer.selectors.dropDown));
return await field.isEnabled();
}
async getValueOfField(fieldName: string) {
return await this.getFieldByName(fieldName).getText();
}
async enterTextInInput(fieldName: string, text: string) {
const input = this.getFieldByName(fieldName);
await input.clear();
return await input.sendKeys(text);
}
async typeTextInInput(fieldName: string, text: string) {
const input = this.getFieldByName(fieldName);
await input.clear();
return await Utils.typeInField(input, text);
}
getButton(button: string) {
return this.component.element(by.cssContainingText(InfoDrawer.selectors.metadataTabAction, button));
}
async isButtonDisplayed(button: string) {
return browser.isElementPresent(this.getButton(button));
}
async isButtonEnabled(button: string) {
return await this.getButton(button).isEnabled();
}
async clickButton(button: string) {
return await this.getButton(button).click();
}
async isButtonDisabled(button: string) {
try {
const disabled = await this.getButton(button).getAttribute('disabled');
return disabled;
} catch (error) {
console.log('----- isButtonDisabled catch: ', error);
}
}
async isMessageDisplayed() {
return await browser.isElementPresent(this.hint);
}
async getMessage() {
return await this.hint.getText();
}
async isErrorDisplayed() {
return await browser.isElementPresent(this.error);
}
async getError() {
return await this.error.getText();
}
// ---------------
async isNameDisplayed() {
return await this.isFieldDisplayed('Name');
}
async isDescriptionDisplayed() {
return await this.isFieldDisplayed('Description');
}
async isVisibilityDisplayed() {
return await this.isFieldDisplayed('Visibility');
}
async isLibraryIdDisplayed() {
return await this.isFieldDisplayed('Library ID');
}
async getName() {
return await this.getValueOfField('Name');
}
async getVisibility() {
return await this.getValueOfField('Visibility');
}
async getLibraryId() {
return await this.getValueOfField('Library ID');
}
async getDescription() {
return await this.getValueOfField('Description');
}
async isNameEnabled() {
return await this.isInputEnabled('Name');
}
async isLibraryIdEnabled() {
return await this.isInputEnabled('Library ID');
}
async isDescriptionEnabled() {
return await this.isInputEnabled('Description');
}
async enterName(name: string) {
return await this.enterTextInInput('Name', name);
}
async typeName(name: string) {
return await this.typeTextInInput('Name', name);
}
async enterDescription(desc: string) {
return await this.enterTextInInput('Description', desc);
}
async typeDescription(desc: string) {
return await this.typeTextInInput('Description', desc);
}
async waitForDropDownToOpen() {
await browser.wait(EC.presenceOf(this.visibilityDropDown), BROWSER_WAIT_TIMEOUT);
}
async waitForDropDownToClose() {
await browser.wait(EC.stalenessOf(browser.$('.mat-option .mat-option-text')), BROWSER_WAIT_TIMEOUT);
}
async setVisibility(visibility: string) {
const val = visibility.toLowerCase();
await this.visibilityDropDown.click();
await this.waitForDropDownToOpen();
if (val === 'public') {
await this.visibilityPublic.click();
} else if (val === 'private') {
await this.visibilityPrivate.click();
} else if (val === 'moderated') {
await this.visibilityModerated.click();
} else {
console.log('----- invalid visibility', val);
}
await this.waitForDropDownToClose();
}
}
+17 -7
View File
@@ -35,7 +35,9 @@ export class Menu extends Component {
icon: '.mat-icon',
uploadFiles: 'app-upload-files',
submenu: 'app-context-menu-item .mat-menu-item'
submenu: 'app-context-menu-item .mat-menu-item',
share: `[data-automation-id='share-action-button']`
};
items: ElementArrayFinder = this.component.all(by.css(Menu.selectors.item));
@@ -43,6 +45,9 @@ export class Menu extends Component {
uploadFiles: ElementFinder = browser.element(by.id(Menu.selectors.uploadFiles));
submenus: ElementArrayFinder = browser.element.all(by.css(Menu.selectors.submenu));
shareAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.share, 'Share'));
shareEditAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.share, 'Shared link settings'));
constructor(ancestor?: ElementFinder) {
super(Menu.selectors.root, ancestor);
}
@@ -58,11 +63,6 @@ export class Menu extends Component {
}
async closeMenu() {
// if (await this.backdrop.isPresent()) {
// return await this.backdrop.click();
// } else {
// return await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform();
// }
return Utils.pressEscape();
}
@@ -148,7 +148,7 @@ export class Menu extends Component {
}
async isMenuItemPresent(title: string) {
return await this.component.element(by.cssContainingText(Menu.selectors.item, title)).isPresent();
return await browser.element(by.cssContainingText(Menu.selectors.item, title)).isPresent();
}
async isSubMenuItemPresent(title: string) {
@@ -172,4 +172,14 @@ export class Menu extends Component {
uploadFile() {
return this.uploadFiles;
}
async clickShareAction() {
const action = this.shareAction;
await action.click();
}
async clickShareEditAction() {
const action = this.shareEditAction;
await action.click();
}
}
+146
View File
@@ -0,0 +1,146 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, browser, by, until, protractor } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Component } from '../component';
export class SearchInput extends Component {
private static selectors = {
root: 'aca-search-input',
searchContainer: '.app-search-container',
searchButton: '.app-search-button',
searchControl: '.app-search-control',
searchInput: 'app-control-input',
searchOptionsArea: 'search-options',
optionCheckbox: '.mat-checkbox'
};
searchButton: ElementFinder = this.component.element(by.css(SearchInput.selectors.searchButton));
searchContainer: ElementFinder = browser.element(by.css(SearchInput.selectors.searchContainer));
searchBar: ElementFinder = browser.element(by.id(SearchInput.selectors.searchInput));
searchOptionsArea: ElementFinder = browser.element(by.id(SearchInput.selectors.searchOptionsArea));
searchFilesOption: ElementFinder = this.searchOptionsArea.element(by.cssContainingText(SearchInput.selectors.optionCheckbox, 'Files'));
searchFoldersOption: ElementFinder = this.searchOptionsArea.element(by.cssContainingText(SearchInput.selectors.optionCheckbox, 'Folders'));
searchLibrariesOption: ElementFinder = this.searchOptionsArea.element(by.cssContainingText(SearchInput.selectors.optionCheckbox, 'Libraries'));
constructor(ancestor?: ElementFinder) {
super(SearchInput.selectors.root, ancestor);
}
async isSearchContainerDisplayed() {
return (await this.searchContainer.isDisplayed()) && (await this.searchButton.isDisplayed());
}
async clickSearchButton() {
await this.searchButton.click();
}
async isOptionsAreaDisplayed() {
await browser.wait(until.elementLocated(by.css(SearchInput.selectors.searchControl)), BROWSER_WAIT_TIMEOUT);
return await browser.isElementPresent(this.searchOptionsArea);
}
async clickFilesOption() {
return await this.searchFilesOption.click();
}
async clickFoldersOption() {
return await this.searchFoldersOption.click();
}
async clickLibrariesOption() {
return await this.searchLibrariesOption.click();
}
async isFilesOptionEnabled() {
const optClass = await this.searchFilesOption.getAttribute('class');
return !optClass.includes('mat-checkbox-disabled');
}
async isFoldersOptionEnabled() {
const optClass = await this.searchFoldersOption.getAttribute('class');
return !optClass.includes('mat-checkbox-disabled');
}
async isLibrariesOptionEnabled() {
const optClass = await this.searchLibrariesOption.getAttribute('class');
return !optClass.includes('mat-checkbox-disabled');
}
async isFilesOptionChecked() {
const optClass = await this.searchFilesOption.getAttribute('class');
return optClass.includes('mat-checkbox-checked');
}
async isFoldersOptionChecked() {
const optClass = await this.searchFoldersOption.getAttribute('class');
return optClass.includes('mat-checkbox-checked');
}
async isLibrariesOptionChecked() {
const optClass = await this.searchLibrariesOption.getAttribute('class');
return optClass.includes('mat-checkbox-checked');
}
async clearOptions() {
if (await this.isFilesOptionChecked()) {
await this.clickFilesOption();
}
if (await this.isFoldersOptionChecked()) {
await this.clickFoldersOption();
}
if (await this.isLibrariesOptionChecked()) {
await this.clickLibrariesOption();
}
}
async checkOnlyFiles() {
await this.clearOptions();
await this.clickFilesOption();
}
async checkOnlyFolders() {
await this.clearOptions();
await this.clickFoldersOption();
}
async checkFilesAndFolders() {
await this.clearOptions();
await this.clickFilesOption();
await this.clickFoldersOption();
}
async checkLibraries() {
await this.clearOptions();
await this.clickLibrariesOption();
}
async searchFor(text: string) {
await this.searchBar.clear();
await this.searchBar.sendKeys(text);
await this.searchBar.sendKeys(protractor.Key.ENTER);
}
}
+61 -18
View File
@@ -23,7 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, ElementArrayFinder, by } from 'protractor';
import { ElementFinder, ElementArrayFinder, by, element } from 'protractor';
import { SIDEBAR_LABELS } from '../../configs';
import { Menu } from '../menu/menu';
import { Component } from '../component';
import { Utils } from '../../utilities/utils';
@@ -31,9 +32,12 @@ import { Utils } from '../../utilities/utils';
export class Sidenav extends Component {
private static selectors = {
root: 'app-sidenav',
link: '.sidenav-menu__item',
label: '.menu__item--label',
activeLink: '.menu__item--active',
link: '.menu__item',
label: '.item--label',
expansion_panel: ".mat-expansion-panel-header",
expansion_panel_content: ".mat-expansion-panel-body",
active: 'item--active',
activeLink: '.item--active',
newButton: '[data-automation-id="create-button"]'
};
@@ -47,6 +51,23 @@ export class Sidenav extends Component {
super(Sidenav.selectors.root, ancestor);
}
private async expandMenu(name: string) {
try{
if (await element(by.cssContainingText('.mat-expanded', name)).isPresent()) {
return Promise.resolve();
} else {
const link = this.getLink(name);
await Utils.waitUntilElementClickable(link);
await link.click();
await element(by.css(Sidenav.selectors.expansion_panel_content)).isPresent();
}
} catch (e) {
console.log('---- sidebar navigation catch expandMenu: ', e);
}
}
async openNewMenu() {
const { menu, newButton } = this;
@@ -54,37 +75,59 @@ export class Sidenav extends Component {
await menu.waitForMenuToOpen();
}
async openCreateDialog() {
async openCreateFolderDialog() {
await this.openNewMenu();
await this.menu.clickMenuItem('Create folder');
}
async isActiveByLabel(label: string) {
const className = await this.getLinkByLabel(label).getAttribute('class');
return className.includes(Sidenav.selectors.activeLink.replace('.', ''));
async openCreateLibraryDialog() {
await this.openNewMenu();
await this.menu.clickMenuItem('Create Library');
}
getLink(label: string) {
return this.component.element(by.cssContainingText(Sidenav.selectors.link, label));
async isActive(name: string) {
const className = await this.getLinkLabel(name).getAttribute('class');
return className.includes(Sidenav.selectors.active);
}
getLinkByLabel(label: string) {
return this.component.element(by.cssContainingText(Sidenav.selectors.label, label));
// return browser.element(by.xpath(`.//*[.="${label}" and class="${Sidenav.selectors.label}"]`))
async childIsActive(name: string) {
const childClass = await this.getLinkLabel(name).element(by.css('span')).getAttribute('class');
return childClass.includes(Sidenav.selectors.active);
}
async getLinkTooltip(label: string) {
return await this.getLink(label).getAttribute('title');
getLink(name: string) {
return this.getLinkLabel(name).element(by.xpath('..'));
}
async navigateToLinkByLabel(label: string) {
getLinkLabel(name: string) {
return this.component.element(by.cssContainingText(Sidenav.selectors.label, name));
}
getActiveLink() {
return this.activeLink;
}
async getLinkTooltip(name: string) {
return await this.getLink(name).getAttribute('title');
}
async navigateToLink(name: string) {
try{
const link = this.getLinkByLabel(label);
const link = this.getLinkLabel(name);
await Utils.waitUntilElementClickable(link);
return await link.click();
} catch (e){
console.log('---- sidebar navigation catch : ', e);
console.log('---- sidebar navigation catch navigateToLink: ', e);
}
}
async isFileLibrariesMenuExpanded() {
return await element(by.cssContainingText('.mat-expanded', SIDEBAR_LABELS.FILE_LIBRARIES)).isPresent();
}
async expandFileLibraries() {
return await this.expandMenu(SIDEBAR_LABELS.FILE_LIBRARIES);
}
}
+24 -1
View File
@@ -30,11 +30,16 @@ import { Component } from '../component';
export class Toolbar extends Component {
private static selectors = {
root: '.adf-toolbar',
button: '.mat-icon-button'
button: 'button',
share: `.mat-icon-button[title='Share']`,
shareEdit: `.mat-icon-button[title='Shared link settings']`
};
menu: Menu = new Menu();
buttons: ElementArrayFinder = this.component.all(by.css(Toolbar.selectors.button));
shareButton: ElementFinder = this.component.element(by.css(Toolbar.selectors.share));
shareEditButton: ElementFinder = this.component.element(by.css(Toolbar.selectors.shareEdit));
constructor(ancestor?: ElementFinder) {
super(Toolbar.selectors.root, ancestor);
@@ -81,4 +86,22 @@ export class Toolbar extends Component {
const btn = this.getButtonByTitleAttribute(title);
await btn.click();
}
async clickShareButton() {
const btn = this.shareButton;
await btn.click();
}
async isShareButtonPresent() {
return await browser.isElementPresent(this.shareButton);
}
async clickShareEditButton() {
const btn = this.shareEditButton;
await btn.click();
}
async isShareEditButtonPresent() {
return await browser.isElementPresent(this.shareEditButton);
}
}
+23 -6
View File
@@ -45,7 +45,8 @@ export const E2E_ROOT_PATH = __dirname;
// Application Routes
export const APP_ROUTES = {
FAVORITES: '/favorites',
FILE_LIBRARIES: '/libraries',
MY_LIBRARIES: '/libraries',
FAVORITE_LIBRARIES: '/favorite/libraries',
LOGIN: '/login',
LOGOUT: '/logout',
PERSONAL_FILES: '/personal-files',
@@ -58,6 +59,8 @@ export const APP_ROUTES = {
export const SIDEBAR_LABELS = {
PERSONAL_FILES: 'Personal Files',
FILE_LIBRARIES: 'File Libraries',
MY_LIBRARIES: 'My Libraries',
FAVORITE_LIBRARIES: 'Favorite Libraries',
SHARED_FILES: 'Shared',
RECENT_FILES: 'Recent Files',
FAVORITES: 'Favorites',
@@ -67,7 +70,9 @@ export const SIDEBAR_LABELS = {
// Page titles
export const PAGE_TITLES = {
VIEWER: 'Preview',
SEARCH: 'Search Results'
SEARCH: 'Search Results',
MY_LIBRARIES: 'My Libraries',
FAVORITE_LIBRARIES: 'Favorite Libraries'
};
// Site visibility
@@ -79,10 +84,22 @@ export const SITE_VISIBILITY = {
// Site roles
export const SITE_ROLES = {
SITE_CONSUMER: 'SiteConsumer',
SITE_COLLABORATOR: 'SiteCollaborator',
SITE_CONTRIBUTOR: 'SiteContributor',
SITE_MANAGER: 'SiteManager'
SITE_CONSUMER: {
ROLE: 'SiteConsumer',
LABEL: 'Consumer'
},
SITE_COLLABORATOR: {
ROLE: 'SiteCollaborator',
LABEL: 'Collaborator'
},
SITE_CONTRIBUTOR: {
ROLE: 'SiteContributor',
LABEL: 'Contributor'
},
SITE_MANAGER: {
ROLE: 'SiteManager',
LABEL: 'Manager'
}
};
export const FILES = {
+77 -1
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { promise } from 'protractor';
import { Header, DataTable, Pagination, Toolbar, Breadcrumb, Sidenav } from '../components/components';
import { SIDEBAR_LABELS } from './../configs';
import { Page } from './page';
export class BrowsingPage extends Page {
@@ -38,4 +38,80 @@ export class BrowsingPage extends Page {
async signOut() {
await this.header.userInfo.signOut();
}
// helper methods
async clickPersonalFilesAndWait() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.PERSONAL_FILES);
await this.dataTable.waitForHeader();
}
async clickPersonalFiles() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.PERSONAL_FILES);
}
async clickFileLibrariesAndWait() {
await this.sidenav.expandFileLibraries();
await this.sidenav.navigateToLink(SIDEBAR_LABELS.MY_LIBRARIES);
await this.dataTable.waitForHeader();
}
async clickFileLibraries() {
await this.sidenav.expandFileLibraries();
await this.sidenav.navigateToLink(SIDEBAR_LABELS.MY_LIBRARIES);
}
async goToFavoriteLibraries() {
await this.sidenav.expandFileLibraries();
await this.sidenav.navigateToLink(SIDEBAR_LABELS.FAVORITE_LIBRARIES);
}
async goToMyLibraries() {
if ( !(await this.sidenav.isFileLibrariesMenuExpanded()) ) {
await this.sidenav.expandFileLibraries();
}
await this.sidenav.navigateToLink(SIDEBAR_LABELS.MY_LIBRARIES);
}
async clickRecentFilesAndWait() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.RECENT_FILES);
await this.dataTable.waitForHeader();
}
async clickRecentFiles() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.RECENT_FILES);
}
async clickSharedFilesAndWait() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.SHARED_FILES);
await this.dataTable.waitForHeader();
}
async clickSharedFiles() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.SHARED_FILES);
}
async clickFavoritesAndWait() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.FAVORITES);
await this.dataTable.waitForHeader();
}
async clickFavorites() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.FAVORITES);
}
async clickTrashAndWait() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.TRASH);
await this.dataTable.waitForHeader();
}
async clickTrash() {
await this.sidenav.navigateToLink(SIDEBAR_LABELS.TRASH);
}
}
+23 -45
View File
@@ -23,22 +23,16 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import {
browser,
element,
by,
ElementFinder,
ExpectedConditions as EC
} from 'protractor';
import { browser, by, ElementFinder, ExpectedConditions as EC, until } from 'protractor';
import { BROWSER_WAIT_TIMEOUT, USE_HASH_STRATEGY } from './../configs';
export abstract class Page {
private locators = {
app: by.css('app-root'),
layout: by.css('app-layout'),
overlay: by.css('.cdk-overlay-container'),
dialogContainer: by.css('.mat-dialog-container'),
snackBarContainer: '.cdk-overlay-pane .mat-snack-bar-container',
private static locators = {
app: 'app-root',
layout: 'app-layout',
overlay: '.cdk-overlay-container',
dialogContainer: '.mat-dialog-container',
snackBarContainer: '.mat-snack-bar-container',
snackBar: '.mat-simple-snackbar',
snackBarAction: '.mat-simple-snackbar-action button',
@@ -47,17 +41,17 @@ export abstract class Page {
genericErrorTitle: '.generic-error__title'
};
public app: ElementFinder = element(this.locators.app);
public layout: ElementFinder = element(this.locators.layout);
public overlay: ElementFinder = element(this.locators.overlay);
snackBar: ElementFinder = browser.$(this.locators.snackBar);
dialogContainer: ElementFinder = element(this.locators.dialogContainer);
snackBarContainer: ElementFinder = browser.$(this.locators.snackBarContainer);
snackBarAction: ElementFinder = browser.$(this.locators.snackBarAction);
app: ElementFinder = browser.element(by.css(Page.locators.app));
layout: ElementFinder = browser.element(by.css(Page.locators.layout));
overlay: ElementFinder = browser.element(by.css(Page.locators.overlay));
snackBar: ElementFinder = browser.element(by.css(Page.locators.snackBar));
dialogContainer: ElementFinder = browser.element(by.css(Page.locators.dialogContainer));
snackBarContainer: ElementFinder = browser.element(by.css(Page.locators.snackBarContainer));
snackBarAction: ElementFinder = browser.element(by.css(Page.locators.snackBarAction));
genericError: ElementFinder = browser.$(this.locators.genericError);
genericErrorIcon: ElementFinder = browser.$(this.locators.genericErrorIcon);
genericErrorTitle: ElementFinder = browser.$(this.locators.genericErrorTitle);
genericError: ElementFinder = browser.element(by.css(Page.locators.genericError));
genericErrorIcon: ElementFinder = browser.element(by.css(Page.locators.genericErrorIcon));
genericErrorTitle: ElementFinder = browser.element(by.css(Page.locators.genericErrorTitle));
constructor(public url: string = '') {}
@@ -76,7 +70,7 @@ export abstract class Page {
}
waitForSnackBarToAppear() {
return browser.wait(EC.visibilityOf(this.snackBarContainer), BROWSER_WAIT_TIMEOUT);
return browser.wait(until.elementLocated(by.css('.mat-snack-bar-container')), BROWSER_WAIT_TIMEOUT, '------- timeout waiting for snackbar to appear');
}
async waitForSnackBarToClose() {
@@ -87,37 +81,20 @@ export abstract class Page {
await browser.wait(EC.visibilityOf(this.dialogContainer), BROWSER_WAIT_TIMEOUT);
}
async waitForDialogToClose() {
await browser.wait(EC.not(EC.visibilityOf(this.dialogContainer)), BROWSER_WAIT_TIMEOUT);
}
async refresh() {
await browser.refresh();
await this.waitForApp();
}
getDialogActionByLabel(label) {
return element(by.cssContainingText('.mat-button-wrapper', label));
}
async isSnackBarDisplayed() {
return await this.snackBar.isDisplayed();
}
async getSnackBarMessage() {
await this.waitForSnackBarToAppear();
return await this.snackBar.getAttribute('innerText');
const elem = await this.waitForSnackBarToAppear();
return await elem.getAttribute('innerText');
}
async clickSnackBarAction() {
try {
// await this.waitForSnackBarToAppear();
// return browser.executeScript(function (elem) {
// elem.click();
// }, this.snackBarAction);
return await this.snackBarAction.click();
const action = browser.wait(until.elementLocated(by.css('.mat-simple-snackbar-action button')), BROWSER_WAIT_TIMEOUT, '------- timeout waiting for snack action to appear');
return await action.click();
} catch (e) {
console.log(e, '.......failed on click snack bar action.........');
}
@@ -130,4 +107,5 @@ export abstract class Page {
async getGenericErrorTitle() {
return await this.genericErrorTitle.getText();
}
}
+1 -1
View File
@@ -25,4 +25,4 @@
export * from './browsing-page';
export * from './login-page';
export * from './logout-page';
export * from './search-results-page';
+55
View File
@@ -0,0 +1,55 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, ElementFinder, by, ExpectedConditions as EC } from 'protractor';
import { BrowsingPage } from './browsing-page';
import { BROWSER_WAIT_TIMEOUT } from '../configs';
export class SearchResultsPage extends BrowsingPage {
private static selectors = {
root: 'aca-search-results',
filter: 'adf-search-filter',
expansionPanel: 'mat-expansion-panel',
size: '#expansion-panel-SEARCH.CATEGORIES.SIZE',
createdDate: '#expansion-panel-SEARCH.CATEGORIES.CREATED_DATE',
modifiedDate: '#expansion-panel-SEARCH.CATEGORIES.MODIFIED_DATE',
fileType: '#expansion-panel-SEARCH.FACET_FIELDS.FILE_TYPE',
creator: '#expansion-panel-SEARCH.CATEGORIES.CREATOR',
modifier: '#expansion-panel-SEARCH.CATEGORIES.MODIFIER',
location: '#expansion-panel-SEARCH.CATEGORIES.LOCATION',
resultsContent: 'adf-search-results__content',
resultsContentHeader: 'adf-search-results__content-header',
resultsInfoText: 'adf-search-results--info-text',
resultsFacets: 'adf-search-results__facets',
sortingPicker: 'adf-sorting-picker'
};
root: ElementFinder = browser.$(SearchResultsPage.selectors.root);
}
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -54,7 +54,6 @@ describe('Context menu actions - multiple selection : ', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable } = page;
const contextMenu = dataTable.menu;
@@ -67,7 +66,7 @@ describe('Context menu actions - multiple selection : ', () => {
folder2Id = (await apis.user.nodes.createFolder(folder2)).entry.id;
await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC);
const docLibId = (await apis.user.sites.getDocLibId(siteName));
const docLibId = await apis.user.sites.getDocLibId(siteName);
await apis.user.nodes.createFile(file1Site, docLibId);
await apis.user.nodes.createFile(file2Site, docLibId);
await apis.user.nodes.createFolder(folder1Site, docLibId);
@@ -94,7 +93,6 @@ describe('Context menu actions - multiple selection : ', () => {
await apis.user.nodes.deleteNodesById([ file1Id, file2Id, folder1Id, folder2Id ]);
await apis.user.sites.deleteSite(siteName);
await apis.user.trashcan.emptyTrash();
await logoutPage.load();
done();
});
@@ -103,15 +101,13 @@ describe('Context menu actions - multiple selection : ', () => {
describe('Generic tests', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.clearSelection();
done();
});
it('Context menu appears on right click on a multiple selection of items - [C286268]', async () => {
await dataTable.selectMultipleItems([ file1, file2 ]);
// await dataTable.rightClickOnItem(file1);
await dataTable.rightClickOnMultipleSelection();
expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed');
});
@@ -131,8 +127,7 @@ describe('Context menu actions - multiple selection : ', () => {
describe('on Personal Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -179,8 +174,7 @@ describe('Context menu actions - multiple selection : ', () => {
describe('on File Libraries', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.clearSelection();
@@ -230,8 +224,7 @@ describe('Context menu actions - multiple selection : ', () => {
describe('on Shared Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -253,8 +246,7 @@ describe('Context menu actions - multiple selection : ', () => {
describe('Recent Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -276,8 +268,7 @@ describe('Context menu actions - multiple selection : ', () => {
describe('Favorites', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.clearSelection();
done();
});
@@ -328,8 +319,7 @@ describe('Context menu actions - multiple selection : ', () => {
describe('Trash', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
await dataTable.clearSelection();
done();
});
@@ -23,9 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, protractor } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -41,13 +40,15 @@ describe('Context menu actions - single selection : ', () => {
const fileSiteUser = `fileUser-${Utils.random()}.txt`;
const folderSiteUser = `folderUser-${Utils.random()}`;
const adminPublic = `admin-public-${Utils.random()}`;
const adminModerated = `admin-moderated-${Utils.random()}`;
const apis = {
admin: new RepoClient(),
user: new RepoClient(username, username)
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable } = page;
const contextMenu = dataTable.menu;
@@ -75,6 +76,12 @@ describe('Context menu actions - single selection : ', () => {
await apis.user.favorites.addFavoriteById('folder', folderUserId);
await apis.user.favorites.waitForApi({ expect: 3 });
await apis.admin.sites.createSite(adminPublic);
await apis.admin.sites.createSite(adminModerated, SITE_VISIBILITY.MODERATED);
await apis.user.favorites.addFavoriteById('site', adminPublic);
await apis.user.favorites.addFavoriteById('site', adminModerated);
await apis.user.sites.requestToJoin(adminModerated);
await loginPage.loginWith(username);
done();
});
@@ -83,8 +90,9 @@ describe('Context menu actions - single selection : ', () => {
await apis.user.nodes.deleteNodeById(fileUserId);
await apis.user.nodes.deleteNodeById(folderUserId);
await apis.user.sites.deleteSite(siteName);
await apis.admin.sites.deleteSite(adminPublic);
await apis.admin.sites.deleteSite(adminModerated);
await apis.user.trashcan.emptyTrash();
await logoutPage.load();
done();
});
@@ -93,8 +101,7 @@ describe('Context menu actions - single selection : ', () => {
describe('Generic tests', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
@@ -126,23 +133,15 @@ describe('Context menu actions - single selection : ', () => {
it('Context menu closes when clicking away from it - [C280619]', async () => {
await dataTable.rightClickOnItem(fileUser);
expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed');
await page.sidenav.activeLink.click();
await page.sidenav.getActiveLink().click();
expect(await dataTable.hasContextMenu()).toBe(false, 'Context menu is displayed');
});
it('Context menu does not appear for a library - [C286276]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await dataTable.rightClickOnItem(siteName);
expect(await dataTable.hasContextMenu()).toBe(false, 'Context menu is displayed for a site');
});
});
describe('on Personal Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -177,11 +176,10 @@ describe('Context menu actions - single selection : ', () => {
});
});
describe('File Libraries', () => {
describe('on File Libraries', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
done();
@@ -217,11 +215,54 @@ describe('Context menu actions - single selection : ', () => {
});
});
describe('Shared Files', () => {
describe('on a library', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await dataTable.clearSelection();
done();
});
it('Available actions when a library is selected - My Libraries - [C290080]', async () => {
await page.goToMyLibraries();
await dataTable.rightClickOnItem(siteName);
expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed');
expect(await contextMenu.isMenuItemPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`);
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`);
expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`);
});
it('Available actions when a library is selected - Favorite Libraries - user is a member - [C290081]', async () => {
await page.goToFavoriteLibraries();
await dataTable.rightClickOnItem(siteName);
expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed');
expect(await contextMenu.isMenuItemPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`);
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`);
expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`);
});
it('Available actions when a library is selected - Favorite Libraries - user is not a member - [C290082]', async () => {
await page.goToFavoriteLibraries();
await dataTable.rightClickOnItem(adminPublic);
expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed');
expect(await contextMenu.isMenuItemPresent('Join')).toBe(true, `Join is not displayed for ${adminPublic}`);
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminPublic}`);
expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminPublic}`);
});
it('Available actions when a library is selected - Favorite Libraries - user requested to join - [C290089]', async () => {
await page.goToFavoriteLibraries();
await dataTable.rightClickOnItem(adminModerated);
expect(await dataTable.hasContextMenu()).toBe(true, 'Context menu is not displayed');
expect(await contextMenu.isMenuItemPresent('Cancel join')).toBe(true, `Cancel join is not displayed for ${adminModerated}`);
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminModerated}`);
expect(await contextMenu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminModerated}`);
});
});
describe('on Shared Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.clickSharedFilesAndWait();
done();
});
@@ -233,8 +274,7 @@ describe('Context menu actions - single selection : ', () => {
expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`);
expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`);
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`);
// TODO: enable this when the action is properly implemented: ACA-92
// expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`);
expect(await contextMenu.isMenuItemPresent('Shared link settings')).toBe(true, `Shared link settings is not displayed for ${fileUser}`);
expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions not displayed for ${fileUser}`);
expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`);
expect(await contextMenu.isMenuItemPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`);
@@ -242,11 +282,10 @@ describe('Context menu actions - single selection : ', () => {
});
});
describe('Recent Files', () => {
describe('on Recent Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
@@ -266,11 +305,10 @@ describe('Context menu actions - single selection : ', () => {
});
});
describe('Favorites', () => {
describe('on Favorites', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -306,11 +344,10 @@ describe('Context menu actions - single selection : ', () => {
});
});
describe('Trash', () => {
describe('on Trash', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
done();
});
+79 -70
View File
@@ -23,12 +23,9 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser } from 'protractor';
import { SIDEBAR_LABELS, SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog';
import { Menu } from '../../components/menu/menu';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -42,7 +39,12 @@ describe('Create folder', () => {
const duplicateFolderName = `folder-${Utils.random()}`;
const nameWithSpaces = ` folder-${Utils.random()} `;
const siteName = `site-private-${Utils.random()}`;
const sitePrivate = `site-private-${Utils.random()}`;
const siteName = `site-${Utils.random()}`;
const folderSite = `folder-site-${Utils.random()}`;
const duplicateFolderSite = `folder-${Utils.random()}`;
let docLibUserSite;
const apis = {
admin: new RepoClient(),
@@ -50,35 +52,39 @@ describe('Create folder', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const personalFilesPage = new BrowsingPage();
const page = new BrowsingPage();
const createDialog = new CreateOrEditFolderDialog();
const { dataTable } = personalFilesPage;
const menu = new Menu();
const { dataTable } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE);
const docLibId = (await apis.admin.sites.getDocLibId(siteName));
await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE);
const docLibId = await apis.admin.sites.getDocLibId(sitePrivate);
await apis.admin.nodes.createFolder(folderName1, docLibId);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER.ROLE);
parentId = (await apis.user.nodes.createFolder(parent)).entry.id;
await apis.user.nodes.createFolder(duplicateFolderName, parentId);
await apis.user.sites.createSite(siteName);
docLibUserSite = await apis.user.sites.getDocLibId(siteName);
await apis.user.nodes.createFolder(duplicateFolderSite, docLibUserSite);
await loginPage.loginWith(username);
done();
});
afterAll(async (done) => {
await apis.admin.sites.deleteSite(siteName);
await apis.admin.sites.deleteSite(sitePrivate);
await apis.user.sites.deleteSite(siteName);
await apis.user.nodes.deleteNodeById(parentId);
await logoutPage.load();
done();
});
describe('on Personal Files', () => {
beforeEach(async (done) => {
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
@@ -87,16 +93,9 @@ describe('Create folder', () => {
done();
});
it('option is enabled when having enough permissions - [C216339]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openNewMenu();
const isEnabled = await menu.getItemByLabel('Create folder').isEnabled();
expect(isEnabled).toBe(true, 'Create folder is not enabled');
});
it('creates new folder with name - [C216341]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(folderName1);
await createDialog.clickCreate();
@@ -107,8 +106,8 @@ describe('Create folder', () => {
});
it('creates new folder with name and description - [C216340]', async (done) => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(folderName2);
await createDialog.enterDescription(folderDescription);
@@ -116,21 +115,14 @@ describe('Create folder', () => {
await createDialog.waitForDialogToClose();
await dataTable.waitForHeader();
expect(await dataTable.getRowByName(folderName2).isPresent()).toBe(true, 'Folder not displayed');
const desc = await apis.user.nodes.getNodeDescription(folderName2, parent);
const desc = await apis.user.nodes.getNodeDescription(folderName2, parentId);
expect(desc).toEqual(folderDescription);
done();
});
it('enabled option tooltip - [C216342]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openNewMenu();
await browser.actions().mouseMove(menu.getItemByLabel('Create folder')).perform();
expect(await menu.getItemTooltip('Create folder')).toContain('Create new folder');
});
it('dialog UI elements - [C216345]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
const dialogTitle = await createDialog.getTitle();
const isFolderNameDisplayed = await createDialog.nameInput.isDisplayed();
@@ -146,8 +138,8 @@ describe('Create folder', () => {
});
it('with empty folder name - [C216346]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.deleteNameWithBackspace();
const isCreateEnabled = await createDialog.createButton.isEnabled();
@@ -158,8 +150,8 @@ describe('Create folder', () => {
});
it('with folder name ending with a dot "." - [C216348]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName('folder-name.');
const isCreateEnabled = await createDialog.createButton.isEnabled();
@@ -172,8 +164,8 @@ describe('Create folder', () => {
it('with folder name containing special characters - [C216347]', async () => {
const namesWithSpecialChars = [ 'a*a', 'a"a', 'a<a', 'a>a', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ];
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
for (const name of namesWithSpecialChars) {
@@ -184,8 +176,8 @@ describe('Create folder', () => {
});
it('with folder name containing only spaces - [C280406]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(' ');
const isCreateEnabled = await createDialog.createButton.isEnabled();
@@ -196,29 +188,29 @@ describe('Create folder', () => {
});
it('cancel folder creation - [C216349]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName('test');
await createDialog.enterDescription('test description');
await createDialog.clickCancel();
expect(await createDialog.component.isPresent()).not.toBe(true, 'dialog is not closed');
expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed');
});
it('duplicate folder name - [C216350]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(duplicateFolderName);
await createDialog.clickCreate();
const message = await personalFilesPage.getSnackBarMessage();
const message = await page.getSnackBarMessage();
expect(message).toEqual(`There's already a folder with this name. Try a different name.`);
expect(await createDialog.component.isPresent()).toBe(true, 'dialog is not present');
expect(await createDialog.isDialogOpen()).toBe(true, 'dialog is not present');
});
it('trim ending spaces from folder name - [C216351]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(parent);
await personalFilesPage.sidenav.openCreateDialog();
await page.dataTable.doubleClickOnRowByName(parent);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(nameWithSpaces);
await createDialog.clickCreate();
@@ -233,8 +225,7 @@ describe('Create folder', () => {
const fileLibrariesPage = new BrowsingPage();
beforeEach(async (done) => {
await fileLibrariesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await fileLibrariesPage.clickFileLibrariesAndWait();
done();
});
@@ -243,21 +234,39 @@ describe('Create folder', () => {
done();
});
it('option is disabled when not enough permissions - [C280397]', async () => {
await fileLibrariesPage.dataTable.doubleClickOnRowByName(siteName);
await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderName1);
await fileLibrariesPage.sidenav.openNewMenu();
const isEnabled = await menu.getItemByLabel('Create folder').isEnabled();
expect(isEnabled).toBe(false, 'Create folder is not disabled');
it('creates new folder with name and description - [C280394]', async () => {
await page.dataTable.doubleClickOnRowByName(siteName);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(folderSite);
await createDialog.enterDescription(folderDescription);
await createDialog.clickCreate();
await createDialog.waitForDialogToClose();
await dataTable.waitForHeader();
expect(await dataTable.getRowByName(folderSite).isPresent()).toBe(true, 'Folder not displayed');
const desc = await apis.user.nodes.getNodeDescription(folderSite, docLibUserSite);
expect(desc).toEqual(folderDescription);
});
it('disabled option tooltip - [C280398]', async () => {
await fileLibrariesPage.dataTable.doubleClickOnRowByName(siteName);
await fileLibrariesPage.dataTable.doubleClickOnRowByName(folderName1);
await fileLibrariesPage.sidenav.openNewMenu();
await browser.actions().mouseMove(menu.getItemByLabel('Create folder')).perform();
const tooltip = await menu.getItemTooltip('Create folder');
expect(tooltip).toContain(`Folders cannot be created whilst viewing the current items`);
it('cancel folder creation - [C280403]', async () => {
await page.dataTable.doubleClickOnRowByName(siteName);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName('test');
await createDialog.enterDescription('test description');
await createDialog.clickCancel();
expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed');
});
it('duplicate folder name - [C280404]', async () => {
await page.dataTable.doubleClickOnRowByName(siteName);
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(duplicateFolderSite);
await createDialog.clickCreate();
const message = await page.getSnackBarMessage();
expect(message).toEqual(`There's already a folder with this name. Try a different name.`);
expect(await createDialog.isDialogOpen()).toBe(true, 'dialog is not present');
});
});
+215
View File
@@ -0,0 +1,215 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { CreateLibraryDialog } from '../../components/dialog/create-library-dialog';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
describe('Create library', () => {
const username = `user-${Utils.random()}`;
const site1Name = `site1-${Utils.random()}`;
const site2Name = `site2-${Utils.random()}`;
const site3Name = `site3-${Utils.random()}`;
const site4 = {
name: `site4-${Utils.random()}`,
id: `site4-id-${Utils.random()}`,
description: 'site4 description'
}
const duplicateSite = {
name: `duplicate-${Utils.random()}`,
id: `duplicate-${Utils.random()}`
}
const siteInTrash = {
name: `site-trash-${Utils.random()}`,
id: `site-trash-id-${Utils.random()}`
}
const apis = {
admin: new RepoClient(),
user: new RepoClient(username, username)
};
const loginPage = new LoginPage();
const page = new BrowsingPage();
const createDialog = new CreateLibraryDialog();
const { dataTable } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.user.sites.createSite(duplicateSite.name, SITE_VISIBILITY.PRIVATE, '', duplicateSite.id);
await apis.user.sites.createSite(siteInTrash.name, SITE_VISIBILITY.PUBLIC, '', siteInTrash.id);
await apis.user.sites.deleteSite(siteInTrash.id, false);
await loginPage.loginWith(username);
done();
});
afterEach(async (done) => {
await Utils.pressEscape();
done();
});
afterAll(async (done) => {
await apis.user.sites.deleteAllUserSites();
done();
});
it('Create Library dialog UI - [C280024]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
expect(await createDialog.getTitle()).toMatch('Create Library');
expect(await createDialog.isNameDisplayed()).toBe(true, 'Name input is not displayed');
expect(await createDialog.isLibraryIdDisplayed()).toBe(true, 'Library ID input is not displayed');
expect(await createDialog.isDescriptionDisplayed()).toBe(true, 'Description field is not displayed');
expect(await createDialog.isPublicDisplayed()).toBe(true, 'Public option is not displayed');
expect(await createDialog.isModeratedDisplayed()).toBe(true, 'Moderated option is not displayed');
expect(await createDialog.isPrivateDisplayed()).toBe(true, 'Private option is not displayed');
expect(await createDialog.isPublicChecked()).toBe(true, 'Public option not checked');
expect(await createDialog.isCreateEnabled()).toBe(false, 'Create button is not disabled');
expect(await createDialog.isCancelEnabled()).toBe(true, 'Cancel button is not enabled');
});
it('Create a public library - [C280025]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(site1Name);
await createDialog.clickCreate();
await createDialog.waitForDialogToClose();
const current = await page.breadcrumb.getCurrentItemName();
expect(current).toEqual(site1Name, `Not navigated into ${site1Name}`);
await page.clickFileLibrariesAndWait();
expect(await dataTable.getRowByName(site1Name).isPresent()).toBe(true, `${site1Name} not in the list`);
expect(await apis.user.sites.getVisibility(site1Name)).toEqual(SITE_VISIBILITY.PUBLIC);
});
it('Create a moderated library - [C289880]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(site2Name);
await createDialog.selectModerated();
await createDialog.clickCreate();
await createDialog.waitForDialogToClose();
const current = await page.breadcrumb.getCurrentItemName();
expect(current).toEqual(site2Name, `Not navigated into ${site2Name}`);
await page.clickFileLibrariesAndWait();
expect(await dataTable.getRowByName(site2Name).isPresent()).toBe(true, `${site2Name} not in the list`);
expect(await apis.user.sites.getVisibility(site2Name)).toEqual(SITE_VISIBILITY.MODERATED);
});
it('Create a private library - [C289881]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(site3Name);
await createDialog.selectPrivate();
await createDialog.clickCreate();
await createDialog.waitForDialogToClose();
const current = await page.breadcrumb.getCurrentItemName();
expect(current).toEqual(site3Name, `Not navigated into ${site3Name}`);
await page.clickFileLibrariesAndWait();
expect(await dataTable.getRowByName(site3Name).isPresent()).toBe(true, `${site3Name} not in the list`);
expect(await apis.user.sites.getVisibility(site3Name)).toEqual(SITE_VISIBILITY.PRIVATE);
});
it('Create a library with a given ID and description - [C289882]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(site4.name);
await createDialog.enterLibraryId(site4.id);
await createDialog.enterDescription(site4.description);
await createDialog.selectPublic();
await createDialog.clickCreate();
await createDialog.waitForDialogToClose();
const current = await page.breadcrumb.getCurrentItemName();
expect(current).toEqual(site4.name, `Not navigated into ${site4.name}`);
await page.clickFileLibrariesAndWait();
expect(await dataTable.getRowByName(site4.name).isPresent()).toBe(true, `${site4.name} not in the list`);
expect(await apis.user.sites.getVisibility(site4.id)).toEqual(SITE_VISIBILITY.PUBLIC);
expect(await apis.user.sites.getDescription(site4.id)).toEqual(site4.description);
});
it('Duplicate library ID - [C280027]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(duplicateSite.name);
await createDialog.enterLibraryId(duplicateSite.id);
expect(await createDialog.isCreateEnabled()).toBe(false, 'Create button not disabled');
const err = await createDialog.getErrorMessage();
expect(err).toEqual(`This Library ID isn't available. Try a different Library ID.`);
});
it('Create library using the ID of a library from the Trashcan - [C280028]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(siteInTrash.name);
await createDialog.enterLibraryId(siteInTrash.id);
await createDialog.clickCreate();
const err = await createDialog.getErrorMessage();
expect(err).toEqual(`This Library ID is already used. Check the trashcan.`);
});
it('Cancel button - [C280029]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName('test site');
await createDialog.enterDescription('test description');
await createDialog.clickCancel();
expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed');
});
it('Library ID cannot contain special characters - [C280026]', async () => {
const idWithSpecialChars = [ 'a*a', 'a"a', 'a<a', 'a>a', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ];
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName('test site');
for (const id of idWithSpecialChars) {
await createDialog.enterLibraryId(id);
expect(await createDialog.isCreateEnabled()).toBe(false, 'Create button is not disabled');
expect(await createDialog.getErrorMessage()).toContain(`Use numbers and letters only`);
}
});
it('Create 2 libraries with same name but different IDs - [C280030]', async () => {
await page.sidenav.openCreateLibraryDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(duplicateSite.name);
await createDialog.enterLibraryId(`${duplicateSite.id}-2`);
await createDialog.clickCreate();
await createDialog.waitForDialogToClose();
const current = await page.breadcrumb.getCurrentItemName();
expect(current).toEqual(duplicateSite.name, `Not navigated into ${duplicateSite.name}`);
await page.clickFileLibrariesAndWait();
expect(await dataTable.getRowByName(`${duplicateSite.name} (${duplicateSite.id}-2)`).isPresent()).toBe(true, `${duplicateSite.name} not in the list`);
expect(await apis.user.sites.getTitle(`${duplicateSite.id}-2`)).toEqual(duplicateSite.name);
});
});
+45 -41
View File
@@ -24,8 +24,7 @@
*/
import { browser } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -38,7 +37,6 @@ describe('Delete and undo delete', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
@@ -48,7 +46,7 @@ describe('Delete and undo delete', () => {
});
afterAll(async (done) => {
await apis.admin.trashcan.emptyTrash();
await apis.user.trashcan.emptyTrash();
done();
});
@@ -61,17 +59,24 @@ describe('Delete and undo delete', () => {
const file4 = `file4-${Utils.random()}.txt`; let file4Id;
const folder1 = `folder1-${Utils.random()}`; let folder1Id;
const folder2 = `folder2-${Utils.random()}`; let folder2Id;
const fileLocked1 = `fileLocked-${Utils.random()}.txt`; let fileLocked1Id;
const folder3 = `folder3-${Utils.random()}`; let folder3Id;
const fileLocked1 = `fileLocked1-${Utils.random()}.txt`; let fileLocked1Id;
const fileLocked2 = `fileLocked2-${Utils.random()}.txt`; let fileLocked2Id;
beforeAll(async (done) => {
file1Id = (await apis.user.nodes.createFile(file1)).entry.id;
file2Id = (await apis.user.nodes.createFile(file2)).entry.id;
folder1Id = (await apis.user.nodes.createFolder(folder1)).entry.id;
folder2Id = (await apis.user.nodes.createFolder(folder2)).entry.id;
await apis.user.nodes.createFile(file3, folder1Id);
file4Id = (await apis.user.nodes.createFile(file4, folder2Id)).entry.id;
await apis.user.nodes.lockFile(file4Id);
folder3Id = (await apis.user.nodes.createFolder(folder3)).entry.id;
fileLocked2Id = (await apis.user.nodes.createFile(fileLocked2, folder3Id)).entry.id;
await apis.user.nodes.lockFile(fileLocked2Id);
fileLocked1Id = (await apis.user.nodes.createFile(fileLocked1)).entry.id;
await apis.user.nodes.lockFile(fileLocked1Id);
@@ -80,8 +85,7 @@ describe('Delete and undo delete', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
@@ -93,8 +97,8 @@ describe('Delete and undo delete', () => {
afterAll(async (done) => {
await apis.user.nodes.unlockFile(file4Id);
await apis.user.nodes.unlockFile(fileLocked1Id);
await logoutPage.load();
await apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id, fileLocked1Id]);
await apis.user.nodes.unlockFile(fileLocked2Id);
await apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id, folder3Id, fileLocked1Id]);
await apis.user.search.waitForApi(username, {expect: 0});
done();
});
@@ -110,7 +114,7 @@ describe('Delete and undo delete', () => {
expect(await dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not removed from list');
items--;
expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(file1).isPresent()).toBe(true, 'Item is not in trash');
await apis.user.trashcan.restore(file1Id);
@@ -128,7 +132,7 @@ describe('Delete and undo delete', () => {
expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} was not removed from list`);
items = items - 2;
expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(file1).isPresent()).toBe(true, `${file1} is not in trash`);
expect(await dataTable.getRowByName(file2).isPresent()).toBe(true, `${file2} is not in trash`);
@@ -145,7 +149,7 @@ describe('Delete and undo delete', () => {
expect(await dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not removed from list');
items--;
expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(folder1).isPresent()).toBe(true, 'Item is not in trash');
expect(await dataTable.getRowByName(file3).isPresent()).toBe(false, 'Item is in trash');
@@ -159,7 +163,7 @@ describe('Delete and undo delete', () => {
const message = await page.getSnackBarMessage();
expect(message).toContain(`${folder2} couldn't be deleted`);
expect(await dataTable.getRowByName(folder2).isPresent()).toBe(true, 'Item was removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(folder2).isPresent()).toBe(false, 'Item is in trash');
expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, 'Item is in trash');
});
@@ -175,7 +179,7 @@ describe('Delete and undo delete', () => {
});
it('notification on multiple items deletion - all items fail to delete - [C217130]', async () => {
await dataTable.selectMultipleItems([fileLocked1, folder2]);
await dataTable.selectMultipleItems([folder3, folder2]);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Delete');
const message = await page.getSnackBarMessage();
@@ -258,8 +262,7 @@ describe('Delete and undo delete', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
done();
});
@@ -269,7 +272,6 @@ describe('Delete and undo delete', () => {
});
afterAll(async (done) => {
await logoutPage.load();
await apis.user.nodes.deleteNodesById([sharedFile1Id, sharedFile2Id, sharedFile3Id, sharedFile4Id]);
await apis.user.search.waitForApi(username, {expect: 0});
done();
@@ -282,7 +284,7 @@ describe('Delete and undo delete', () => {
const message = await page.getSnackBarMessage();
expect(message).toContain(`${sharedFile1} deleted`);
expect(await dataTable.getRowByName(sharedFile1).isPresent()).toBe(false, 'Item was not removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(sharedFile1).isPresent()).toBe(true, 'Item is not in trash');
await apis.user.trashcan.restore(sharedFile1Id);
@@ -298,7 +300,7 @@ describe('Delete and undo delete', () => {
expect(message).toContain(`Deleted 2 items`);
expect(await dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, `${sharedFile2} was not removed from list`);
expect(await dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not removed from list`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(sharedFile2).isPresent()).toBe(true, `${sharedFile2} is not in trash`);
expect(await dataTable.getRowByName(sharedFile3).isPresent()).toBe(true, `${sharedFile3} is not in trash`);
@@ -323,7 +325,7 @@ describe('Delete and undo delete', () => {
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Delete');
await page.clickSnackBarAction();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(sharedFile2).isPresent()).toBe(false, 'Item was not restored');
});
@@ -332,7 +334,7 @@ describe('Delete and undo delete', () => {
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Delete');
await page.clickSnackBarAction();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(sharedFile3).isPresent()).toBe(false, `${sharedFile3} was not restored`);
expect(await dataTable.getRowByName(sharedFile4).isPresent()).toBe(false, `${sharedFile4} was not restored`);
});
@@ -345,31 +347,35 @@ describe('Delete and undo delete', () => {
const favoriteFile4 = `favFile4-${Utils.random()}.txt`; let favoriteFile4Id;
const favoriteFolder1 = `favFolder1-${Utils.random()}`; let favoriteFolder1Id;
const favoriteFolder2 = `favFolder2-${Utils.random()}`; let favoriteFolder2Id;
const favoriteFileLocked1 = `favFileLocked-${Utils.random()}.txt`; let favoriteFileLocked1Id;
const favoriteFolder3 = `favFolder3-${Utils.random()}`; let favoriteFolder3Id;
const favoriteFileLocked1 = `favFileLocked1-${Utils.random()}.txt`; let favoriteFileLocked1Id;
const favoriteFileLocked2 = `favFileLocked2-${Utils.random()}.txt`; let favoriteFileLocked2Id;
beforeAll(async (done) => {
favoriteFile1Id = (await apis.user.nodes.createFile(favoriteFile1)).entry.id;
favoriteFile2Id = (await apis.user.nodes.createFile(favoriteFile2)).entry.id;
favoriteFolder1Id = (await apis.user.nodes.createFolder(favoriteFolder1)).entry.id;
favoriteFolder2Id = (await apis.user.nodes.createFolder(favoriteFolder2)).entry.id;
favoriteFolder3Id = (await apis.user.nodes.createFolder(favoriteFolder3)).entry.id;
await apis.user.nodes.createFile(favoriteFile3, favoriteFolder1Id);
favoriteFile4Id = (await apis.user.nodes.createFile(favoriteFile4, favoriteFolder2Id)).entry.id;
favoriteFileLocked2Id = (await apis.user.nodes.createFile(favoriteFileLocked2, favoriteFolder3Id)).entry.id;
await apis.user.nodes.lockFile(favoriteFile4Id);
await apis.user.nodes.lockFile(favoriteFileLocked2Id);
favoriteFileLocked1Id = (await apis.user.nodes.createFile(favoriteFileLocked1)).entry.id;
await apis.user.nodes.lockFile(favoriteFileLocked1Id);
await apis.user.favorites.addFavoritesByIds('file', [ favoriteFile1Id, favoriteFile2Id, favoriteFileLocked1Id ]);
await apis.user.favorites.addFavoritesByIds('folder', [ favoriteFolder1Id, favoriteFolder2Id ]);
await apis.user.favorites.waitForApi({ expect: 5 });
await apis.user.favorites.addFavoritesByIds('folder', [ favoriteFolder1Id, favoriteFolder2Id, favoriteFolder3Id ]);
await apis.user.favorites.waitForApi({ expect: 6 });
await loginPage.loginWith(username);
done();
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -381,9 +387,9 @@ describe('Delete and undo delete', () => {
afterAll(async (done) => {
await apis.user.nodes.unlockFile(favoriteFile4Id);
await apis.user.nodes.unlockFile(favoriteFileLocked1Id);
await logoutPage.load();
await apis.user.nodes.unlockFile(favoriteFileLocked2Id);
await apis.user.nodes.deleteNodesById([
favoriteFile1Id, favoriteFile2Id, favoriteFolder1Id, favoriteFolder2Id, favoriteFileLocked1Id
favoriteFile1Id, favoriteFile2Id, favoriteFolder1Id, favoriteFolder2Id, favoriteFileLocked1Id, favoriteFolder3Id
]);
await apis.user.search.waitForApi(username, {expect: 0});
done();
@@ -400,7 +406,7 @@ describe('Delete and undo delete', () => {
expect(await dataTable.getRowByName(favoriteFile1).isPresent()).toBe(false, 'Item was not removed from list');
items--;
expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, 'Item is not in trash');
await apis.user.trashcan.restore(favoriteFile1Id);
@@ -418,7 +424,7 @@ describe('Delete and undo delete', () => {
expect(await dataTable.getRowByName(favoriteFile2).isPresent()).toBe(false, `${favoriteFile2} was not removed from list`);
items = items - 2;
expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(favoriteFile1).isPresent()).toBe(true, `${favoriteFile1} is not in trash`);
expect(await dataTable.getRowByName(favoriteFile2).isPresent()).toBe(true, `${favoriteFile2} is not in trash`);
@@ -434,7 +440,7 @@ describe('Delete and undo delete', () => {
expect(await dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(false, 'Item was not removed from list');
items--;
expect(await page.pagination.range.getText()).toContain(`1-${items} of ${items}`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(favoriteFolder1).isPresent()).toBe(true, 'Item is not in trash');
expect(await dataTable.getRowByName(favoriteFile3).isPresent()).toBe(false, 'Item is in trash');
@@ -448,7 +454,7 @@ describe('Delete and undo delete', () => {
const message = await page.getSnackBarMessage();
expect(message).toContain(`${favoriteFolder2} couldn't be deleted`);
expect(await dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(true, 'Item was removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(favoriteFolder2).isPresent()).toBe(false, 'Item is in trash');
expect(await dataTable.getRowByName(favoriteFile4).isPresent()).toBe(false, 'Item is in trash');
});
@@ -464,7 +470,7 @@ describe('Delete and undo delete', () => {
});
it('notification on multiple items deletion - all items fail to delete - [C280521]', async () => {
await dataTable.selectMultipleItems([favoriteFileLocked1, favoriteFolder2]);
await dataTable.selectMultipleItems([favoriteFolder3, favoriteFolder2]);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Delete');
const message = await page.getSnackBarMessage();
@@ -542,7 +548,7 @@ describe('Delete and undo delete', () => {
await loginPage.loginWith(username);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await page.clickRecentFiles();
const empty = await dataTable.isEmptyList();
if (empty) {
await browser.sleep(6000);
@@ -553,8 +559,7 @@ describe('Delete and undo delete', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
@@ -564,7 +569,6 @@ describe('Delete and undo delete', () => {
});
afterAll(async (done) => {
await logoutPage.load();
await apis.user.nodes.deleteNodesById([recentFile1Id, recentFile2Id, recentFile3Id, recentFile4Id]);
done();
});
@@ -576,7 +580,7 @@ describe('Delete and undo delete', () => {
const message = await page.getSnackBarMessage();
expect(message).toContain(`${recentFile1} deleted`);
expect(await dataTable.getRowByName(recentFile1).isPresent()).toBe(false, 'Item was not removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(recentFile1).isPresent()).toBe(true, 'Item is not in trash');
await apis.user.trashcan.restore(recentFile1Id);
@@ -591,7 +595,7 @@ describe('Delete and undo delete', () => {
expect(message).toContain(`Deleted 2 items`);
expect(await dataTable.getRowByName(recentFile2).isPresent()).toBe(false, `${recentFile2} was not removed from list`);
expect(await dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} was not removed from list`);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(recentFile2).isPresent()).toBe(true, `${recentFile2} is not in trash`);
expect(await dataTable.getRowByName(recentFile3).isPresent()).toBe(true, `${recentFile3} is not in trash`);
@@ -620,7 +624,7 @@ describe('Delete and undo delete', () => {
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Delete');
await page.clickSnackBarAction();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(recentFile2).isPresent()).toBe(false, 'Item is in Trash');
});
@@ -633,7 +637,7 @@ describe('Delete and undo delete', () => {
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Delete');
await page.clickSnackBarAction();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.getRowByName(recentFile3).isPresent()).toBe(false, `${recentFile3} is in Trash`);
expect(await dataTable.getRowByName(recentFile4).isPresent()).toBe(false, `${recentFile4} is in Trash`);
});
+184 -93
View File
@@ -23,9 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { protractor, browser } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SIDEBAR_LABELS, SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog';
import { Utils } from '../../utilities/utils';
@@ -43,7 +42,17 @@ describe('Edit folder', () => {
const folderNameEdited = `folder-${Utils.random()}`;
const folderDescriptionEdited = 'description edited';
const siteName = `site-private-${Utils.random()}`;
const sitePrivate = `site-private-${Utils.random()}`;
const siteName = `site-${Utils.random()}`;
const folderSite = `folder-site-${Utils.random()}`;
const folderSiteToEdit = `folder-site-${Utils.random()}`; let folderSiteToEditId;
const duplicateFolderSite = `folder-${Utils.random()}`;
let docLibUserSite;
const folderFavorite = `folder-fav-${Utils.random()}`; let folderFavoriteId;
const folderFavoriteToEdit = `folder-fav-${Utils.random()}`; let folderFavoriteToEditId;
const folderFavoriteDuplicate = `folder-fav-${Utils.random()}`; let folderFavoriteDuplicateId;
const apis = {
admin: new RepoClient(),
@@ -51,51 +60,57 @@ describe('Edit folder', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const personalFilesPage = new BrowsingPage();
const page = new BrowsingPage();
const editDialog = new CreateOrEditFolderDialog();
const { dataTable } = personalFilesPage;
const editButton = personalFilesPage.toolbar.getButtonByTitleAttribute('Edit');
const { dataTable } = page;
const editButton = page.toolbar.getButtonByTitleAttribute('Edit');
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE);
const docLibId = await apis.admin.sites.getDocLibId(siteName);
await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE);
const docLibId = await apis.admin.sites.getDocLibId(sitePrivate);
await apis.admin.nodes.createFolder(folderName, docLibId);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER.ROLE);
parentId = (await apis.user.nodes.createFolder(parent)).entry.id;
await apis.user.nodes.createFolder(folderName, parentId, '', folderDescription);
await apis.user.nodes.createFolder(folderNameToEdit, parentId);
await apis.user.nodes.createFolder(duplicateFolderName, parentId);
await apis.user.sites.createSite(siteName);
docLibUserSite = await apis.user.sites.getDocLibId(siteName);
await apis.user.nodes.createFolder(folderSite, docLibUserSite);
folderSiteToEditId = (await apis.user.nodes.createFolder(folderSiteToEdit, docLibUserSite)).entry.id;
await apis.user.nodes.createFolder(duplicateFolderSite, docLibUserSite);
folderFavoriteId = (await apis.user.nodes.createFolder(folderFavorite)).entry.id;
folderFavoriteToEditId = (await apis.user.nodes.createFolder(folderFavoriteToEdit)).entry.id;
folderFavoriteDuplicateId = (await apis.user.nodes.createFolder(folderFavoriteDuplicate)).entry.id;
await apis.user.favorites.addFavoriteById('folder', folderFavoriteId);
await apis.user.favorites.addFavoriteById('folder', folderFavoriteToEditId);
await apis.user.favorites.addFavoriteById('folder', folderFavoriteDuplicateId);
await loginPage.loginWith(username);
done();
});
beforeEach(async (done) => {
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await dataTable.doubleClickOnRowByName(parent);
await dataTable.waitForHeader();
done();
});
afterEach(async (done) => {
await browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
done();
});
afterAll(async (done) => {
await Promise.all([
apis.admin.sites.deleteSite(siteName),
apis.user.nodes.deleteNodeById(parentId),
logoutPage.load()
apis.admin.sites.deleteSite(sitePrivate),
apis.user.sites.deleteSite(siteName),
apis.user.nodes.deleteNodesById([ parentId, folderFavoriteToEditId, folderFavoriteDuplicateId ])
]);
done();
});
afterEach(async (done) => {
await Utils.pressEscape();
done();
});
it('dialog UI defaults - [C216331]', async () => {
await dataTable.doubleClickOnRowByName(parent);
await dataTable.selectItem(folderName);
await editButton.click();
expect(await editDialog.getTitle()).toEqual('Edit folder');
@@ -105,77 +120,153 @@ describe('Edit folder', () => {
expect(await editDialog.cancelButton.isEnabled()).toBe(true, 'cancel button is not enabled');
});
it('properties are modified when pressing OK - [C216335]', async (done) => {
await dataTable.selectItem(folderNameToEdit);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterDescription(folderDescriptionEdited);
await editDialog.enterName(folderNameEdited);
await editDialog.clickUpdate();
await editDialog.waitForDialogToClose();
await dataTable.waitForHeader();
expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed');
const desc = await apis.user.nodes.getNodeDescription(folderNameEdited, parent);
expect(desc).toEqual(folderDescriptionEdited);
done();
describe('on Personal Files', () => {
beforeEach(async (done) => {
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(parent);
await dataTable.waitForHeader();
done();
});
it('properties are modified when pressing OK - [C216335]', async (done) => {
await dataTable.selectItem(folderNameToEdit);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterDescription(folderDescriptionEdited);
await editDialog.enterName(folderNameEdited);
await editDialog.clickUpdate();
await editDialog.waitForDialogToClose();
await dataTable.waitForHeader();
expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed');
const desc = await apis.user.nodes.getNodeDescription(folderNameEdited, parentId);
expect(desc).toEqual(folderDescriptionEdited);
done();
});
it('with empty folder name - [C216332]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.deleteNameWithBackspace();
expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled');
expect(await editDialog.getValidationMessage()).toMatch('Folder name is required');
});
it('with name with special characters - [C216333]', async () => {
const namesWithSpecialChars = [ 'a*a', 'a"a', 'a<a', 'a>a', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ];
await dataTable.selectItem(folderName);
await editButton.click();
for (const name of namesWithSpecialChars) {
await editDialog.enterName(name);
expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not disabled');
expect(await editDialog.getValidationMessage()).toContain(`Folder name can't contain these characters`);
}
});
it('with name ending with a dot - [C216334]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.nameInput.sendKeys('.');
expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled');
expect(await editDialog.getValidationMessage()).toMatch(`Folder name can't end with a period .`);
});
it('Cancel button - [C216336]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.clickCancel();
expect(await editDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed');
});
it('with duplicate folder name - [C216337]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterName(duplicateFolderName);
await editDialog.clickUpdate();
const message = await page.getSnackBarMessage();
expect(message).toEqual(`There's already a folder with this name. Try a different name.`);
expect(await editDialog.isDialogOpen()).toBe(true, 'dialog is not present');
});
it('trim ending spaces - [C216338]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.nameInput.sendKeys(' ');
await editDialog.clickUpdate();
await editDialog.waitForDialogToClose();
expect(await page.snackBar.isPresent()).not.toBe(true, 'notification appears');
expect(await dataTable.getRowByName(folderName).isPresent()).toBe(true, 'Folder not displayed in list view');
});
});
it('with empty folder name - [C216332]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.deleteNameWithBackspace();
expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled');
expect(await editDialog.getValidationMessage()).toMatch('Folder name is required');
describe('on Favorites', () => {
beforeEach(async (done) => {
await page.clickFavoritesAndWait();
done();
});
it('properties are modified when pressing OK - [C280384]', async (done) => {
await dataTable.selectItem(folderFavoriteToEdit);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterDescription(folderDescriptionEdited);
await editDialog.enterName(folderNameEdited);
await editDialog.clickUpdate();
await editDialog.waitForDialogToClose();
await dataTable.waitForHeader();
expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed');
const desc = await apis.user.nodes.getNodeProperty(folderFavoriteToEditId, 'cm:description');
expect(desc).toEqual(folderDescriptionEdited);
done();
});
it('with duplicate folder name - [C280386]', async () => {
await dataTable.selectItem(folderFavorite);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterName(folderFavoriteDuplicate);
await editDialog.clickUpdate();
const message = await page.getSnackBarMessage();
expect(message).toEqual(`There's already a folder with this name. Try a different name.`);
expect(await editDialog.isDialogOpen()).toBe(true, 'dialog is not present');
});
});
it('with name with special characters - [C216333]', async () => {
const namesWithSpecialChars = [ 'a*a', 'a"a', 'a<a', 'a>a', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ];
describe('on My Libraries', () => {
beforeEach(async (done) => {
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
done();
});
await dataTable.selectItem(folderName);
await editButton.click();
it('properties are modified when pressing OK - [C280509]', async (done) => {
await dataTable.selectItem(folderSiteToEdit);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterDescription(folderDescriptionEdited);
await editDialog.enterName(folderNameEdited);
await editDialog.clickUpdate();
await editDialog.waitForDialogToClose();
await dataTable.waitForHeader();
expect(await dataTable.getRowByName(folderNameEdited).isPresent()).toBe(true, 'Folder not displayed');
const desc = await apis.user.nodes.getNodeProperty(folderSiteToEditId, 'cm:description');
expect(desc).toEqual(folderDescriptionEdited);
done();
});
for (const name of namesWithSpecialChars) {
await editDialog.enterName(name);
expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not disabled');
expect(await editDialog.getValidationMessage()).toContain(`Folder name can't contain these characters`);
}
});
it('with name ending with a dot - [C216334]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.nameInput.sendKeys('.');
expect(await editDialog.updateButton.isEnabled()).toBe(false, 'upload button is not enabled');
expect(await editDialog.getValidationMessage()).toMatch(`Folder name can't end with a period .`);
});
it('Cancel button - [C216336]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.clickCancel();
expect(await editDialog.component.isPresent()).not.toBe(true, 'dialog is not closed');
});
it('with duplicate folder name - [C216337]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterName(duplicateFolderName);
await editDialog.clickUpdate();
const message = await personalFilesPage.getSnackBarMessage();
expect(message).toEqual(`There's already a folder with this name. Try a different name.`);
expect(await editDialog.component.isPresent()).toBe(true, 'dialog is not present');
});
it('trim ending spaces - [C216338]', async () => {
await dataTable.selectItem(folderName);
await editButton.click();
await editDialog.nameInput.sendKeys(' ');
await editDialog.clickUpdate();
await editDialog.waitForDialogToClose();
expect(await personalFilesPage.snackBar.isPresent()).not.toBe(true, 'notification appears');
expect(await dataTable.getRowByName(folderName).isPresent()).toBe(true, 'Folder not displayed in list view');
it('with duplicate folder name - [C280511]', async () => {
await dataTable.selectItem(folderSite);
await editButton.click();
await editDialog.waitForDialogToOpen();
await editDialog.enterName(duplicateFolderSite);
await editDialog.clickUpdate();
const message = await page.getSnackBarMessage();
expect(message).toEqual(`There's already a folder with this name. Try a different name.`);
expect(await editDialog.isDialogOpen()).toBe(true, 'dialog is not present');
});
});
});
+187
View File
@@ -0,0 +1,187 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { ConfirmDialog } from './../../components/components';
import { RepoClient } from '../../utilities/repo-client/repo-client';
describe('Library actions', () => {
const username = `user-${Utils.random()}`;
const sitePublic1Admin = `admin-public1-${Utils.random()}`;
const sitePublic2Admin = `admin-public2-${Utils.random()}`;
const sitePublic3Admin = `admin-public3-${Utils.random()}`;
const sitePublic4Admin = `admin-public4-${Utils.random()}`;
const siteModerated1Admin = `admin-moderated1-${Utils.random()}`;
const siteModerated2Admin = `admin-moderated2-${Utils.random()}`;
const sitePublicUser = `user-public-${Utils.random()}`;
const apis = {
admin: new RepoClient(),
user: new RepoClient(username, username)
};
const loginPage = new LoginPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
const confirmDialog = new ConfirmDialog();
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(sitePublic1Admin);
await apis.admin.sites.createSite(sitePublic2Admin);
await apis.admin.sites.createSite(sitePublic3Admin);
await apis.admin.sites.createSite(sitePublic4Admin);
await apis.admin.sites.createSite(siteModerated1Admin, SITE_VISIBILITY.MODERATED);
await apis.admin.sites.createSite(siteModerated2Admin, SITE_VISIBILITY.MODERATED);
await apis.user.sites.createSite(sitePublicUser);
await apis.admin.sites.addSiteMember(sitePublic2Admin, username, SITE_ROLES.SITE_COLLABORATOR.ROLE);
await apis.admin.sites.addSiteMember(sitePublic3Admin, username, SITE_ROLES.SITE_MANAGER.ROLE);
await apis.admin.sites.addSiteMember(sitePublic4Admin, username, SITE_ROLES.SITE_MANAGER.ROLE);
await apis.user.favorites.addFavoriteById('site', sitePublic1Admin);
await apis.user.favorites.addFavoriteById('site', sitePublic3Admin);
await apis.user.favorites.addFavoriteById('site', siteModerated1Admin);
await apis.user.favorites.addFavoriteById('site', siteModerated2Admin);
await apis.user.sites.requestToJoin(siteModerated2Admin);
await loginPage.loginWith(username);
done();
});
afterEach(async (done) => {
await Utils.pressEscape();
await dataTable.clearSelection();
done();
});
afterAll(async (done) => {
await apis.admin.sites.deleteSite(sitePublic1Admin);
await apis.admin.sites.deleteSite(sitePublic2Admin);
await apis.admin.sites.deleteSite(sitePublic3Admin);
await apis.admin.sites.deleteSite(sitePublic4Admin);
await apis.admin.sites.deleteSite(siteModerated1Admin);
await apis.admin.sites.deleteSite(siteModerated2Admin);
await apis.user.sites.deleteSite(sitePublicUser);
done();
});
it('Join a public library - Favorite Libraries - [C290105]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(sitePublic1Admin);
await toolbar.clickButton('Join');
const role = await dataTable.getLibraryRole(sitePublic1Admin);
expect(role).toEqual('Consumer');
});
it('Join a moderated library - Favorite Libraries - [C290109]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(siteModerated1Admin);
await toolbar.clickButton('Join');
const role = await dataTable.getLibraryRole(siteModerated1Admin);
expect(role).toEqual('');
const hasJoinRequest = await apis.user.sites.hasMembershipRequest(siteModerated1Admin);
expect(hasJoinRequest).toBe(true, `Join request does not exist on ${siteModerated1Admin}`);
});
it('Leave a library - My Libraries - [C290106]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(sitePublic2Admin);
await toolbar.clickButton('Leave library');
await page.waitForDialog();
await confirmDialog.clickButton('OK');
const text = await page.getSnackBarMessage();
expect(text).toEqual(`You have left the library`);
expect(await dataTable.getRowByName(sitePublic2Admin).isPresent()).toBe(false, `${sitePublic2Admin} is displayed`);
});
it('Leave a library - Favorite Libraries - [C290110]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(sitePublic3Admin);
await toolbar.clickButton('Leave library');
await page.waitForDialog();
await confirmDialog.clickButton('OK');
const text = await page.getSnackBarMessage();
expect(text).toEqual(`You have left the library`);
expect(await dataTable.getRowByName(sitePublic3Admin).isPresent()).toBe(true, `${sitePublic3Admin} is not displayed`);
});
it('Confirmation dialog UI - [C290136]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(sitePublic4Admin);
await toolbar.clickButton('Leave library');
await page.waitForDialog();
expect(await confirmDialog.isDialogOpen()).toBe(true, 'Confirm delete dialog not open');
expect(await confirmDialog.getTitle()).toContain('Leave this library?');
expect(await confirmDialog.getText()).toContain('Leaving will remove your access.');
expect(await confirmDialog.isButtonEnabled('OK')).toBe(true, 'OK button is not enabled');
expect(await confirmDialog.isButtonEnabled('Cancel')).toBe(true, 'Cancel button is not enabled');
});
it('Cancel Leave library - [C290111]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(sitePublic4Admin);
await toolbar.clickButton('Leave library');
await page.waitForDialog();
expect(await confirmDialog.isButtonEnabled('Cancel')).toBe(true, 'Cancel button is not enabled');
await confirmDialog.clickButton('Cancel');
expect(await dataTable.getRowByName(sitePublic4Admin).isPresent()).toBe(true, `${sitePublic4Admin} was deleted`);
});
it('Leave a library - failure notification - [C290107]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(sitePublicUser);
await toolbar.clickButton('Leave library');
await page.waitForDialog();
await confirmDialog.clickButton('OK');
const text = await page.getSnackBarMessage();
expect(text).toEqual(`Cannot leave this library`);
});
it('Cancel join - Favorite Libraries - [C290108]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(siteModerated2Admin);
await toolbar.clickButton('Cancel join request');
const text = await page.getSnackBarMessage();
expect(text).toEqual(`Canceled the request to join the library`);
const hasJoinRequest = await apis.user.sites.hasMembershipRequest(siteModerated2Admin);
expect(hasJoinRequest).toBe(false, `Join request exists on ${siteModerated2Admin}`);
});
});
+64 -15
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -52,7 +52,6 @@ describe('Mark items as favorites', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
@@ -90,8 +89,7 @@ describe('Mark items as favorites', () => {
afterAll(async (done) => {
await Promise.all([
apis.user.nodes.deleteNodesById([ file1Id, file2Id, file3Id, file4Id, folder1Id]),
apis.user.sites.deleteSite(siteName),
logoutPage.load()
apis.user.sites.deleteSite(siteName)
]);
done();
});
@@ -101,8 +99,7 @@ describe('Mark items as favorites', () => {
describe('on Personal Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
@@ -208,8 +205,7 @@ describe('Mark items as favorites', () => {
describe('on Recent Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
@@ -290,8 +286,7 @@ describe('Mark items as favorites', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
done();
});
@@ -368,8 +363,7 @@ describe('Mark items as favorites', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -413,8 +407,7 @@ describe('Mark items as favorites', () => {
describe ('on File Libraries', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await page.dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await page.dataTable.doubleClickOnRowByName(siteName);
await page.dataTable.waitForHeader();
done();
@@ -501,4 +494,60 @@ describe('Mark items as favorites', () => {
await apis.user.favorites.waitForApi({ expect: 5 });
});
});
describe('on a library', () => {
const adminSite1 = `adminSite1-${Utils.random()}`;
const adminSite2 = `adminSite2-${Utils.random()}`;
const adminSite3 = `adminSite3-${Utils.random()}`;
beforeAll(async (done) => {
await apis.admin.sites.createSite(adminSite1);
await apis.admin.sites.createSite(adminSite2);
await apis.admin.sites.createSite(adminSite3);
await apis.admin.sites.addSiteMember(adminSite1, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.admin.sites.addSiteMember(adminSite2, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.admin.sites.addSiteMember(adminSite3, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.user.favorites.addFavoriteById('site', adminSite2);
await apis.user.favorites.addFavoriteById('site', adminSite3);
done();
});
beforeEach(async (done) => {
await Utils.pressEscape();
done();
});
afterAll(async (done) => {
await apis.admin.sites.deleteSite(adminSite1);
await apis.admin.sites.deleteSite(adminSite2);
await apis.admin.sites.deleteSite(adminSite3);
done();
});
it('Mark a library as favorite - [C289974]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(adminSite1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Favorite');
expect(await apis.user.favorites.isFavoriteWithRetry(adminSite1, { expect: true })).toBe(true, `${adminSite1} not favorite`);
});
it('Remove a library from favorites - on My Libraries - [C289975]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(adminSite2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Favorite');
expect(await apis.user.favorites.isFavoriteWithRetry(adminSite2, { expect: false })).toBe(false, `${adminSite2} still favorite`);
});
it('Remove a library from favorites - on Favorite Libraries - [C289976]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(adminSite3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Favorite');
expect(await dataTable.getRowByName(adminSite3).isPresent()).toBe(false, `${adminSite3} is displayed`);
expect(await apis.user.favorites.isFavoriteWithRetry(adminSite3, { expect: false })).toBe(false, `${adminSite3} still favorite`);
});
});
});
+181
View File
@@ -0,0 +1,181 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_ROLES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
describe('New menu', () => {
const username = `user-${Utils.random()}`;
const siteUser = `site-user-${Utils.random()}`;
const siteAdmin = `site-admin-${Utils.random()}`;
const apis = {
admin: new RepoClient(),
user: new RepoClient(username, username)
};
const loginPage = new LoginPage();
const page = new BrowsingPage();
const { dataTable, sidenav } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(siteAdmin);
await apis.admin.sites.addSiteMember(siteAdmin, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.user.sites.createSite(siteUser);
await loginPage.loginWith(username);
done();
});
afterAll(async (done) => {
await apis.user.sites.deleteSite(siteUser);
await apis.admin.sites.deleteSite(siteAdmin);
done();
});
afterEach(async (done) => {
await Utils.pressEscape();
done();
});
it('has correct actions - [C286524]', async () => {
await page.clickPersonalFiles();
await sidenav.openNewMenu();
expect(await sidenav.menu.isMenuItemPresent('Create folder')).toBe(true, 'Create Folder option not present');
expect(await sidenav.menu.isMenuItemPresent('Create Library')).toBe(true, 'Create Library option not present');
expect(await sidenav.menu.isMenuItemPresent('Upload file')).toBe(true, 'Upload File option not present');
expect(await sidenav.menu.isMenuItemPresent('Upload folder')).toBe(true, 'Upload Folder option not present');
expect(await sidenav.menu.getItemByLabel('Create Library').isEnabled()).toBe(true, 'Create Library is not enabled');
});
it('Create folder is enabled when having enough permissions - Personal Files - [C216339]', async () => {
await page.clickPersonalFiles();
await page.sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Create folder').isEnabled();
expect(isEnabled).toBe(true, 'Create folder is not enabled');
});
it('Create folder is enabled when having enough permissions - File Libraries - [C280393]', async () => {
await page.clickFileLibraries();
await page.dataTable.doubleClickOnRowByName(siteUser);
await page.sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Create folder').isEnabled();
expect(isEnabled).toBe(true, 'Create folder is not enabled');
});
it('Create folder is disabled when not enough permissions - [C280397]', async () => {
await page.clickFileLibraries();
await dataTable.doubleClickOnRowByName(siteAdmin);
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Create folder').isEnabled();
expect(isEnabled).toBe(false, 'Create folder is not disabled');
});
it('Upload File option is enabled when having enough permissions - on Personal Files - [C217145]', async () => {
await page.clickPersonalFiles();
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Upload file').isEnabled();
expect(isEnabled).toBe(true, 'Upload file is not enabled in Personal Files');
});
it('Upload File option is enabled when having permissions - on File Libraries - [C290142]', async () => {
await page.clickFileLibraries();
await dataTable.doubleClickOnRowByName(siteUser);
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Upload file').isEnabled();
expect(isEnabled).toBe(true, 'Upload file is not enabled in File Libraries');
});
it('Upload File option is disabled when user cannot create content in that location - [C217146]', async () => {
await page.clickFileLibraries();
await dataTable.doubleClickOnRowByName(siteAdmin);
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Upload file').isEnabled();
expect(isEnabled).toBe(false, 'Upload file is not disabled');
});
it('Upload Folder option is enabled when having enough permissions - on Personal Files - [C213196]', async () => {
await page.clickPersonalFiles();
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Upload folder').isEnabled();
expect(isEnabled).toBe(true, 'Upload folder is not enabled in Personal Files');
});
it('Upload Folder option is enabled when having permissions - on File Libraries - [C290146]', async () => {
await page.clickFileLibraries();
await dataTable.doubleClickOnRowByName(siteUser);
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Upload folder').isEnabled();
expect(isEnabled).toBe(true, 'Upload folder is not enabled in File Libraries');
});
it('Upload Folder option is disabled when user cannot create content in that location - [C213193]', async () => {
await page.clickFileLibraries();
await dataTable.doubleClickOnRowByName(siteAdmin);
await sidenav.openNewMenu();
const isEnabled = await sidenav.menu.getItemByLabel('Upload folder').isEnabled();
expect(isEnabled).toBe(false, 'Upload folder is not disabled');
});
it('Create folder enabled button tooltip - [C216342]', async () => {
await page.clickPersonalFiles();
await sidenav.openNewMenu();
const tooltip = await sidenav.menu.getItemTooltip('Create folder');
expect(tooltip).toContain('Create new folder');
});
it('Create folder disabled button tooltip - [C280398]', async () => {
await page.clickFileLibraries();
await dataTable.doubleClickOnRowByName(siteAdmin);
await sidenav.openNewMenu();
const tooltip = await sidenav.menu.getItemTooltip('Create folder');
expect(tooltip).toContain(`Folders cannot be created whilst viewing the current items`);
});
it('Create Library button tooltip - [C286526]', async () => {
await sidenav.openNewMenu();
const tooltip = await sidenav.menu.getItemTooltip('Create Library');
expect(tooltip).toContain('Create a new File Library');
});
});
+31 -27
View File
@@ -23,9 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { ConfirmDialog } from './../../components/components';
import { SIDEBAR_LABELS } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -41,15 +40,16 @@ describe('Permanently delete from Trash', () => {
const folder2 = `folder2-${Utils.random()}`;
let foldersIds;
const site = `site-${Utils.random()}`;
const apis = {
admin: new RepoClient(),
user: new RepoClient(username, username)
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const trashPage = new BrowsingPage();
const { dataTable, toolbar } = trashPage;
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
const confirmDialog = new ConfirmDialog();
@@ -57,61 +57,65 @@ describe('Permanently delete from Trash', () => {
await apis.admin.people.createUser({ username });
filesIds = (await apis.user.nodes.createFiles([ file1, file2, file3 ])).list.entries.map(entries => entries.entry.id);
foldersIds = (await apis.user.nodes.createFolders([ folder1, folder2 ])).list.entries.map(entries => entries.entry.id);
await apis.user.sites.createSite(site);
await apis.user.nodes.deleteNodesById(filesIds, false);
await apis.user.nodes.deleteNodesById(foldersIds, false);
await apis.user.sites.deleteSite(site, false);
await loginPage.loginWith(username);
done();
});
beforeEach(async (done) => {
await trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
done();
});
afterAll(async (done) => {
await Promise.all([
apis.user.trashcan.emptyTrash(),
logoutPage.load()
]);
await apis.user.trashcan.emptyTrash();
done();
});
it('delete file - [C217091]', async () => {
it('delete a file - [C217091]', async () => {
await dataTable.selectItem(file1);
await toolbar.getButtonByTitleAttribute('Permanently delete').click();
await trashPage.waitForDialog();
// await trashPage.getDialogActionByLabel('Delete').click();
// await trashPage.waitForDialogToClose();
await page.waitForDialog();
await confirmDialog.clickButton('Delete');
const text = await trashPage.getSnackBarMessage();
const text = await page.getSnackBarMessage();
expect(text).toEqual(`${file1} deleted`);
expect(await dataTable.getRowByName(file1).isPresent()).toBe(false, 'Item was not deleted');
});
it('delete folder - [C280416]', async () => {
it('delete a folder - [C280416]', async () => {
await dataTable.selectItem(folder1);
await toolbar.getButtonByTitleAttribute('Permanently delete').click();
await trashPage.waitForDialog();
// await trashPage.getDialogActionByLabel('Delete').click();
// await trashPage.waitForDialogToClose();
await page.waitForDialog();
await confirmDialog.clickButton('Delete');
const text = await trashPage.getSnackBarMessage();
const text = await page.getSnackBarMessage();
expect(text).toEqual(`${folder1} deleted`);
expect(await dataTable.getRowByName(folder1).isPresent()).toBe(false, 'Item was not deleted');
});
it('delete a library - [C290103]', async () => {
await dataTable.selectItem(site);
await toolbar.getButtonByTitleAttribute('Permanently delete').click();
await page.waitForDialog();
await confirmDialog.clickButton('Delete');
const text = await page.getSnackBarMessage();
expect(text).toEqual(`${site} deleted`);
expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} was not deleted`);
});
it('delete multiple items - [C280417]', async () => {
await dataTable.selectMultipleItems([ file2, folder2 ]);
await toolbar.getButtonByTitleAttribute('Permanently delete').click();
await trashPage.waitForDialog();
// await trashPage.getDialogActionByLabel('Delete').click();
// await trashPage.waitForDialogToClose();
await page.waitForDialog();
await confirmDialog.clickButton('Delete');
const text = await trashPage.getSnackBarMessage();
const text = await page.getSnackBarMessage();
expect(text).toEqual(`2 items deleted`);
expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, 'Item was not deleted');
@@ -121,7 +125,7 @@ describe('Permanently delete from Trash', () => {
it('Confirmation dialog UI - [C269113]', async () => {
await dataTable.selectItem(file3);
await toolbar.getButtonByTitleAttribute('Permanently delete').click();
await trashPage.waitForDialog();
await page.waitForDialog();
expect(await confirmDialog.isDialogOpen()).toBe(true, 'Confirm delete dialog not open');
expect(await confirmDialog.getTitle()).toContain('Delete from trash');
@@ -136,7 +140,7 @@ describe('Permanently delete from Trash', () => {
it('Keep action cancels the deletion - [C269115]', async () => {
await dataTable.selectItem(file3);
await toolbar.getButtonByTitleAttribute('Permanently delete').click();
await trashPage.waitForDialog();
await page.waitForDialog();
expect(await confirmDialog.isButtonEnabled('Keep')).toBe(true, 'KEEP button is not enabled');
await confirmDialog.clickButton('Keep');
+30 -30
View File
@@ -24,8 +24,8 @@
*/
import { browser } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { APP_ROUTES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { APP_ROUTES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -38,7 +38,6 @@ describe('Restore from Trash', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
@@ -49,31 +48,29 @@ describe('Restore from Trash', () => {
});
afterAll(async (done) => {
await Promise.all([
apis.admin.trashcan.emptyTrash(),
logoutPage.load()
]);
await apis.user.trashcan.emptyTrash();
done();
});
xit('');
describe('successful restore', () => {
const file = `file-${Utils.random()}.txt`;
let fileId;
const folder = `folder-${Utils.random()}`;
let folderId;
const file = `file-${Utils.random()}.txt`; let fileId;
const folder = `folder-${Utils.random()}`; let folderId;
const site = `site-${Utils.random()}`;
beforeAll(async (done) => {
fileId = (await apis.user.nodes.createFile(file)).entry.id;
folderId = (await apis.user.nodes.createFolder(folder)).entry.id;
await apis.user.sites.createSite(site);
await apis.user.nodes.deleteNodesById([fileId, folderId], false);
await apis.user.sites.deleteSite(site, false);
done();
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
done();
});
@@ -89,8 +86,7 @@ describe('Restore from Trash', () => {
expect(text).toContain(`${file} restored`);
expect(text).toContain(`View`);
expect(await dataTable.getRowByName(file).isPresent()).toBe(false, 'Item was not removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
expect(await page.dataTable.getRowByName(file).isPresent()).toBe(true, 'Item not displayed in list');
await apis.user.nodes.deleteNodeById(fileId, false);
@@ -103,13 +99,23 @@ describe('Restore from Trash', () => {
expect(text).toContain(`${folder} restored`);
expect(text).toContain(`View`);
expect(await dataTable.getRowByName(folder).isPresent()).toBe(false, 'Item was not removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
expect(await page.dataTable.getRowByName(folder).isPresent()).toBe(true, 'Item not displayed in list');
await apis.user.nodes.deleteNodeById(folderId, false);
});
it('restore library - [C290104]', async () => {
await dataTable.selectItem(site);
await toolbar.getButtonByTitleAttribute('Restore').click();
const text = await page.getSnackBarMessage();
expect(text).toContain(`${site} restored`);
expect(text).toContain(`View`);
expect(await dataTable.getRowByName(site).isPresent()).toBe(false, `${site} was not removed from list`);
await page.clickFileLibrariesAndWait();
expect(await page.dataTable.getRowByName(site).isPresent()).toBe(true, `${site} not displayed in list`);
});
it('restore multiple items - [C217182]', async () => {
await dataTable.selectMultipleItems([file, folder]);
await toolbar.getButtonByTitleAttribute('Restore').click();
@@ -118,8 +124,7 @@ describe('Restore from Trash', () => {
expect(text).not.toContain(`View`);
expect(await dataTable.getRowByName(file).isPresent()).toBe(false, 'Item was not removed from list');
expect(await dataTable.getRowByName(folder).isPresent()).toBe(false, 'Item was not removed from list');
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
expect(await page.dataTable.getRowByName(file).isPresent()).toBe(true, 'Item not displayed in list');
expect(await page.dataTable.getRowByName(folder).isPresent()).toBe(true, 'Item not displayed in list');
@@ -131,7 +136,7 @@ describe('Restore from Trash', () => {
await toolbar.getButtonByTitleAttribute('Restore').click();
await page.clickSnackBarAction();
await page.dataTable.waitForHeader();
expect(await page.sidenav.isActiveByLabel('Personal Files')).toBe(true, 'Personal Files sidebar link not active');
expect(await page.sidenav.isActive('Personal Files')).toBe(true, 'Personal Files sidebar link not active');
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES);
await apis.user.nodes.deleteNodeById(fileId, false);
@@ -164,8 +169,7 @@ describe('Restore from Trash', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
done();
});
@@ -178,7 +182,7 @@ describe('Restore from Trash', () => {
});
it('Restore a file when another file with same name exists on the restore location - [C217178]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrashAndWait();
await dataTable.selectItem(file1);
await toolbar.getButtonByTitleAttribute('Restore').click();
const text = await page.getSnackBarMessage();
@@ -186,7 +190,7 @@ describe('Restore from Trash', () => {
});
it('Restore a file when original location no longer exists - [C217179]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrashAndWait();
await dataTable.selectItem(file2);
await toolbar.getButtonByTitleAttribute('Restore').click();
const text = await page.getSnackBarMessage();
@@ -239,16 +243,12 @@ describe('Restore from Trash', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
done();
});
afterAll(async (done) => {
await Promise.all([
apis.user.trashcan.emptyTrash(),
logoutPage.load()
]);
await apis.user.trashcan.emptyTrash();
done();
});
+63 -115
View File
@@ -24,8 +24,8 @@
*/
import { browser } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { ShareDialog } from '../../components/dialog/share-dialog';
import { Viewer } from '../../components/viewer/viewer';
@@ -44,7 +44,6 @@ describe('Share a file', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
const shareDialog = new ShareDialog();
@@ -61,10 +60,7 @@ describe('Share a file', () => {
});
afterAll(async (done) => {
await Promise.all([
apis.user.nodes.deleteNodeById(parentId),
logoutPage.load()
]);
await apis.user.nodes.deleteNodeById(parentId);
done();
});
@@ -97,8 +93,7 @@ describe('Share a file', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(parent);
await dataTable.waitForHeader();
done();
@@ -125,8 +120,7 @@ describe('Share a file', () => {
it('Share dialog default values - [C286327]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`);
@@ -142,8 +136,7 @@ describe('Share a file', () => {
it('Close dialog - [C286328]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled');
@@ -153,8 +146,7 @@ describe('Share a file', () => {
it('Share a file - [C286329]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -164,14 +156,13 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`);
});
it('Copy shared file URL - [C286330]', async () => {
await dataTable.selectItem(file4);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
expect(url).toContain('/preview/s/');
@@ -188,8 +179,7 @@ describe('Share a file', () => {
it('Share a file with expiration date - [C286332]', async () => {
await dataTable.selectItem(file5);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
await shareDialog.clickExpirationToggle();
@@ -210,8 +200,7 @@ describe('Share a file', () => {
it('Expire date is displayed correctly - [C286337]', async () => {
await dataTable.selectItem(file6);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate');
@@ -222,8 +211,7 @@ describe('Share a file', () => {
it('Disable the share link expiration - [C286333]', async () => {
await dataTable.selectItem(file7);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked');
@@ -239,8 +227,7 @@ describe('Share a file', () => {
it('Shared file URL is not changed when Share dialog is closed and opened again - [C286335]', async () => {
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url1 = await shareDialog.getLinkUrl();
await shareDialog.clickClose();
@@ -248,8 +235,7 @@ describe('Share a file', () => {
await page.dataTable.clearSelection();
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url2 = await shareDialog.getLinkUrl();
@@ -258,7 +244,7 @@ describe('Share a file', () => {
it('Share a file from the context menu - [C286345]', async () => {
await dataTable.rightClickOnItem(file9);
await contextMenu.clickMenuItem('Share');
await contextMenu.clickShareAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -268,7 +254,7 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`);
});
});
@@ -309,8 +295,7 @@ describe('Share a file', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.doubleClickOnRowByName(parentInSite);
@@ -331,8 +316,7 @@ describe('Share a file', () => {
it('Share dialog default values - [C286639]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`);
@@ -348,8 +332,7 @@ describe('Share a file', () => {
it('Close dialog - [C286640]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled');
@@ -359,8 +342,7 @@ describe('Share a file', () => {
it('Share a file - [C286641]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -370,14 +352,13 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`);
});
it('Copy shared file URL - [C286642]', async () => {
await dataTable.selectItem(file4);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
expect(url).toContain('/preview/s/');
@@ -394,8 +375,7 @@ describe('Share a file', () => {
it('Share a file with expiration date - [C286643]', async () => {
await dataTable.selectItem(file5);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
await shareDialog.clickExpirationToggle();
@@ -416,8 +396,7 @@ describe('Share a file', () => {
it('Expire date is displayed correctly - [C286644]', async () => {
await dataTable.selectItem(file6);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate');
@@ -428,8 +407,7 @@ describe('Share a file', () => {
it('Disable the share link expiration - [C286645]', async () => {
await dataTable.selectItem(file7);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked');
@@ -445,8 +423,7 @@ describe('Share a file', () => {
it('Shared file URL is not changed when Share dialog is closed and opened again - [C286646]', async () => {
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url1 = await shareDialog.getLinkUrl();
await shareDialog.clickClose();
@@ -454,8 +431,7 @@ describe('Share a file', () => {
await page.dataTable.clearSelection();
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url2 = await shareDialog.getLinkUrl();
@@ -464,7 +440,7 @@ describe('Share a file', () => {
it('Share a file from the context menu - [C286647]', async () => {
await dataTable.rightClickOnItem(file9);
await contextMenu.clickMenuItem('Share');
await contextMenu.clickShareAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -474,7 +450,7 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`);
});
});
@@ -509,8 +485,7 @@ describe('Share a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
@@ -535,8 +510,7 @@ describe('Share a file', () => {
it('Share dialog default values - [C286657]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`);
@@ -552,8 +526,7 @@ describe('Share a file', () => {
it('Close dialog - [C286658]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled');
@@ -563,8 +536,7 @@ describe('Share a file', () => {
it('Share a file - [C286659]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -574,14 +546,13 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`);
});
it('Copy shared file URL - [C286660]', async () => {
await dataTable.selectItem(file4);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
expect(url).toContain('/preview/s/');
@@ -598,8 +569,7 @@ describe('Share a file', () => {
it('Share a file with expiration date - [C286661]', async () => {
await dataTable.selectItem(file5);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
await shareDialog.clickExpirationToggle();
@@ -620,8 +590,7 @@ describe('Share a file', () => {
it('Expire date is displayed correctly - [C286662]', async () => {
await dataTable.selectItem(file6);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate');
@@ -632,8 +601,7 @@ describe('Share a file', () => {
it('Disable the share link expiration - [C286663]', async () => {
await dataTable.selectItem(file7);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked');
@@ -649,8 +617,7 @@ describe('Share a file', () => {
it('Shared file URL is not changed when Share dialog is closed and opened again - [C286664]', async () => {
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url1 = await shareDialog.getLinkUrl();
await shareDialog.clickClose();
@@ -658,8 +625,7 @@ describe('Share a file', () => {
await page.dataTable.clearSelection();
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url2 = await shareDialog.getLinkUrl();
@@ -668,7 +634,7 @@ describe('Share a file', () => {
it('Share a file from the context menu - [C286665]', async () => {
await dataTable.rightClickOnItem(file9);
await contextMenu.clickMenuItem('Share');
await contextMenu.clickShareAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -678,7 +644,7 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`);
});
});
@@ -715,8 +681,7 @@ describe('Share a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
done();
});
@@ -739,8 +704,7 @@ describe('Share a file', () => {
it('Share dialog default values - [C286648]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`);
@@ -756,8 +720,7 @@ describe('Share a file', () => {
it('Close dialog - [C286649]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled');
@@ -767,8 +730,7 @@ describe('Share a file', () => {
it('Copy shared file URL - [C286651]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
expect(url).toContain('/preview/s/');
@@ -785,8 +747,7 @@ describe('Share a file', () => {
it('Expire date is displayed correctly - [C286653]', async () => {
await dataTable.selectItem(file4);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const expireProperty = await apis.user.nodes.getNodeProperty(file4Id, 'qshare:expiryDate');
@@ -797,8 +758,7 @@ describe('Share a file', () => {
it('Disable the share link expiration - [C286654]', async () => {
await dataTable.selectItem(file5);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked');
@@ -814,8 +774,7 @@ describe('Share a file', () => {
it('Shared file URL is not changed when Share dialog is closed and opened again - [C286655]', async () => {
await dataTable.selectItem(file6);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url1 = await shareDialog.getLinkUrl();
await shareDialog.clickClose();
@@ -823,8 +782,7 @@ describe('Share a file', () => {
await page.dataTable.clearSelection();
await dataTable.selectItem(file6);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url2 = await shareDialog.getLinkUrl();
@@ -833,7 +791,7 @@ describe('Share a file', () => {
it('Open Share dialog from context menu - [C286656]', async () => {
await dataTable.rightClickOnItem(file7);
await contextMenu.clickMenuItem('Shared link settings');
await contextMenu.clickShareEditAction();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.getTitle()).toEqual(`Share ${file7}`);
@@ -890,8 +848,7 @@ describe('Share a file', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -916,8 +873,7 @@ describe('Share a file', () => {
it('Share dialog default values - [C286666]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`);
@@ -933,8 +889,7 @@ describe('Share a file', () => {
it('Close dialog - [C286667]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled');
@@ -944,8 +899,7 @@ describe('Share a file', () => {
it('Share a file - [C286668]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
@@ -955,14 +909,13 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`);
});
it('Copy shared file URL - [C286669]', async () => {
await dataTable.selectItem(file4);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
expect(url).toContain('/preview/s/');
@@ -979,8 +932,7 @@ describe('Share a file', () => {
it('Share a file with expiration date - [C286670]', async () => {
await dataTable.selectItem(file5);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
await shareDialog.clickExpirationToggle();
@@ -1001,8 +953,7 @@ describe('Share a file', () => {
it('Expire date is displayed correctly - [C286671]', async () => {
await dataTable.selectItem(file6);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate');
@@ -1013,8 +964,7 @@ describe('Share a file', () => {
it('Disable the share link expiration - [C286672]', async () => {
await dataTable.selectItem(file7);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked');
@@ -1030,8 +980,7 @@ describe('Share a file', () => {
it('Shared file URL is not changed when Share dialog is closed and opened again - [C286673]', async () => {
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url1 = await shareDialog.getLinkUrl();
await shareDialog.clickClose();
@@ -1039,8 +988,7 @@ describe('Share a file', () => {
await page.dataTable.clearSelection();
await dataTable.selectItem(file8);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url2 = await shareDialog.getLinkUrl();
@@ -1059,7 +1007,7 @@ describe('Share a file', () => {
expect(url).toContain(sharedId);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`);
});
});
+7 -16
View File
@@ -23,9 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Viewer } from '../../components/viewer/viewer';
import { SIDEBAR_LABELS } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -47,7 +46,6 @@ describe('Single click on item name', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, breadcrumb } = page;
const viewer = new Viewer();
@@ -82,13 +80,11 @@ describe('Single click on item name', () => {
await apis.user.nodes.deleteNodeById(folder1Id);
await apis.user.nodes.deleteNodeById(file1Id);
await apis.user.trashcan.emptyTrash();
await logoutPage.load();
done();
});
it('Hyperlink does not appear for items in the Trash - [C284899]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
expect(await dataTable.hasLinkOnName(deletedFile1)).toBe(false, 'Link on name is present');
expect(await dataTable.hasLinkOnName(deletedFolder1)).toBe(false, 'Link on name is present');
@@ -96,8 +92,7 @@ describe('Single click on item name', () => {
describe('on Personal Files', () => {
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
@@ -122,8 +117,7 @@ describe('Single click on item name', () => {
describe('on File Libraries', () => {
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
done();
});
@@ -141,8 +135,7 @@ describe('Single click on item name', () => {
describe('on Shared Files', () => {
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
done();
});
@@ -161,8 +154,7 @@ describe('Single click on item name', () => {
describe('on Recent Files', () => {
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
@@ -181,8 +173,7 @@ describe('Single click on item name', () => {
describe('on Favorites', () => {
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS, FILES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES, FILES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
import { Viewer } from '../../components/viewer/viewer';
@@ -56,7 +56,6 @@ describe('Granular permissions available actions : ', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
const contextMenu = dataTable.menu;
@@ -78,10 +77,10 @@ describe('Granular permissions available actions : ', () => {
docxFileId = (await apis.admin.upload.uploadFile(docxFile, docLibId)).entry.id;
await apis.admin.sites.addSiteMember(siteName, userManager, SITE_ROLES.SITE_MANAGER);
await apis.admin.sites.addSiteMember(siteName, userConsumer, SITE_ROLES.SITE_CONSUMER);
await apis.admin.sites.addSiteMember(siteName, userManager, SITE_ROLES.SITE_MANAGER.ROLE);
await apis.admin.sites.addSiteMember(siteName, userConsumer, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.admin.nodes.setGranularPermission(file3Id, false, userConsumer, SITE_ROLES.SITE_MANAGER);
await apis.admin.nodes.setGranularPermission(file3Id, false, userConsumer, SITE_ROLES.SITE_MANAGER.ROLE);
await apis.userConsumer.shared.shareFileById(file1Id);
await apis.userConsumer.shared.shareFileById(file2Id);
@@ -99,7 +98,6 @@ describe('Granular permissions available actions : ', () => {
afterAll(async done => {
await apis.admin.sites.deleteSite(siteName);
await logoutPage.load();
done();
});
@@ -112,8 +110,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280476]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([file1, file2]);
@@ -129,8 +126,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Shared Files - [C280477]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.selectMultipleItems([file1, file2]);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`);
@@ -144,8 +140,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C280478]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([file1, file2]);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`);
@@ -167,8 +162,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280455]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectItem(file1);
@@ -186,8 +180,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Shared Files - [C280456]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await page.dataTable.selectItem(file1);
expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`);
@@ -203,8 +196,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C213121]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectItem(file1);
expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${file1}`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${file1}`);
@@ -227,8 +219,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280444]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectItem(folder1);
@@ -246,8 +237,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286266]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectItem(folder1);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is not displayed for ${folder1}`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${folder1}`);
@@ -272,8 +262,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280464]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([file1, file2]);
@@ -289,8 +278,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Shared Files - [C286284]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.selectMultipleItems([file1, file2]);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`);
@@ -304,8 +292,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286285]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([file1, file2]);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`);
@@ -328,8 +315,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280465]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([folder1, folder2]);
@@ -345,8 +331,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286286]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([folder1, folder2]);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`);
@@ -369,8 +354,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280466]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([file1, folder1]);
@@ -386,8 +370,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286287]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([file1, folder1]);
expect(await toolbar.isButtonPresent('View')).toBe(false, `View is displayed for selected files`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for selected files`);
@@ -409,8 +392,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280599]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.rightClickOnItem(file1);
@@ -428,8 +410,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Shared Files - [C286264]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.rightClickOnItem(file1);
expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`);
expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`);
@@ -446,8 +427,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286262]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.rightClickOnItem(file1);
expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not displayed for ${file1}`);
expect(await contextMenu.isMenuItemPresent('View')).toBe(true, `View is not displayed for ${file1}`);
@@ -472,8 +452,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280600]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.rightClickOnItem(folder1);
@@ -490,8 +469,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286263]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.rightClickOnItem(folder1);
expect(await contextMenu.isMenuItemPresent('Download')).toBe(true, `Download is not enabled for ${folder1}`);
// enable when ACA-1737 is done
@@ -517,8 +495,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280647]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([file1, file2]);
@@ -533,8 +510,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Shared Files - [C286283]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.selectMultipleItems([file1, file2]);
await dataTable.rightClickOnMultipleSelection();
expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed');
@@ -547,8 +523,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286280]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([file1, file2]);
await dataTable.rightClickOnMultipleSelection();
expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed');
@@ -570,8 +545,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280666]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([folder1, folder2]);
@@ -586,8 +560,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286281]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([folder1, folder2]);
await dataTable.rightClickOnMultipleSelection();
expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed');
@@ -609,8 +582,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on File Libraries - [C280669]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.selectMultipleItems([file1, folder1]);
@@ -625,8 +597,7 @@ describe('Granular permissions available actions : ', () => {
});
it('on Favorites - [C286282]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectMultipleItems([file1, folder1]);
await dataTable.rightClickOnMultipleSelection();
expect(await contextMenu.isMenuItemPresent('View')).toBe(false, 'View is displayed');
@@ -647,8 +618,7 @@ describe('Granular permissions available actions : ', () => {
});
it('file from File Libraries - [C268128]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.doubleClickOnRowByName(docxFile);
@@ -659,10 +629,11 @@ describe('Granular permissions available actions : ', () => {
expect(await viewerToolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed`);
expect(await viewerToolbar.isButtonPresent('Print')).toBe(true, `Print is not displayed`);
expect(await viewerToolbar.isButtonPresent('Activate full-screen mode')).toBe(true, `Full screen is not displayed`);
expect(await viewerToolbar.isShareEditButtonPresent()).toBe(true, 'Shared link settings is not displayed');
expect(await viewerToolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed`);
await viewerToolbar.openMoreMenu();
expect(await viewerToolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`);
expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed`);
expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(false, `Share is displayed in More actions`);
expect(await viewerToolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`);
expect(await viewerToolbar.menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`);
expect(await viewerToolbar.menu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed`);
@@ -672,8 +643,7 @@ describe('Granular permissions available actions : ', () => {
});
it('file from Shared Files - [C286310]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.doubleClickOnRowByName(docxFile);
expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened');
@@ -694,8 +664,7 @@ describe('Granular permissions available actions : ', () => {
});
it('file from Favorites - [C286311]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.doubleClickOnRowByName(docxFile);
expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened');
@@ -704,10 +673,11 @@ describe('Granular permissions available actions : ', () => {
expect(await viewerToolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed`);
expect(await viewerToolbar.isButtonPresent('Print')).toBe(true, `Print is not displayed`);
expect(await viewerToolbar.isButtonPresent('Activate full-screen mode')).toBe(true, `Full screen is not displayed`);
expect(await viewerToolbar.isShareEditButtonPresent()).toBe(true, 'Shared link settings is not displayed');
expect(await viewerToolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed`);
await viewerToolbar.openMoreMenu();
expect(await viewerToolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`);
expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed`);
expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(false, `Share is displayed in More actions`);
expect(await viewerToolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`);
// TODO: enable when ACA-1737 is done
// expect(await viewerToolbar.menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`);
@@ -24,8 +24,8 @@
*/
import { browser, protractor } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -63,7 +63,6 @@ describe('Toolbar actions - multiple selection : ', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
@@ -105,8 +104,7 @@ describe('Toolbar actions - multiple selection : ', () => {
await Promise.all([
apis.user.nodes.deleteNodesById([file1Id, file2Id, folder1Id, folder2Id]),
apis.user.trashcan.emptyTrash(),
apis.user.sites.deleteSite(siteName),
logoutPage.load()
apis.user.sites.deleteSite(siteName)
]);
done();
});
@@ -116,8 +114,7 @@ describe('Toolbar actions - multiple selection : ', () => {
describe('Personal Files', () => {
beforeEach(async done => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -187,10 +184,8 @@ describe('Toolbar actions - multiple selection : ', () => {
describe('File Libraries', () => {
beforeEach(async done => {
// await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform();
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.clearSelection();
@@ -238,8 +233,7 @@ describe('Toolbar actions - multiple selection : ', () => {
beforeEach(async done => {
// await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform();
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -259,10 +253,8 @@ describe('Toolbar actions - multiple selection : ', () => {
describe('Recent Files', () => {
beforeEach(async done => {
// await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform();
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -284,8 +276,7 @@ describe('Toolbar actions - multiple selection : ', () => {
beforeEach(async done => {
// await browser.actions().mouseMove(browser.$('body'), { x: 0, y: 0 }).click().perform();
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.clearSelection();
done();
});
@@ -330,8 +321,7 @@ describe('Toolbar actions - multiple selection : ', () => {
describe('Trash', () => {
beforeEach(async done => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
await dataTable.clearSelection();
done();
});
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -40,13 +40,15 @@ describe('Toolbar actions - single selection : ', () => {
const fileInSite = `fileAdmin-${Utils.random()}.txt`;
const folderInSite = `folderAdmin-${Utils.random()}`;
const adminPublic = `admin-public-${Utils.random()}`;
const adminModerated = `admin-moderated-${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, toolbar } = page;
@@ -67,13 +69,18 @@ describe('Toolbar actions - single selection : ', () => {
await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PRIVATE);
const docLibId = await apis.user.sites.getDocLibId(siteName);
await apis.user.nodes.createFile(fileInSite, docLibId);
await apis.user.nodes.createFolder(folderInSite, docLibId);
await apis.user.nodes.deleteNodeById(fileForDeleteId, false);
await apis.user.nodes.deleteNodeById(folderForDeleteId, false);
await apis.admin.sites.createSite(adminPublic);
await apis.admin.sites.createSite(adminModerated, SITE_VISIBILITY.MODERATED);
await apis.user.favorites.addFavoriteById('site', adminPublic);
await apis.user.favorites.addFavoriteById('site', adminModerated);
await apis.user.sites.requestToJoin(adminModerated);
await loginPage.loginWith(username);
done();
});
@@ -83,8 +90,9 @@ describe('Toolbar actions - single selection : ', () => {
apis.user.nodes.deleteNodeById(fileUserId),
apis.user.nodes.deleteNodeById(folderUserId),
apis.user.sites.deleteSite(siteName),
apis.user.trashcan.emptyTrash(),
logoutPage.load()
apis.admin.sites.deleteSite(adminPublic),
apis.admin.sites.deleteSite(adminModerated),
apis.user.trashcan.emptyTrash()
]);
done();
});
@@ -92,26 +100,23 @@ describe('Toolbar actions - single selection : ', () => {
xit('');
describe('General tests', () => {
it('actions not displayed for top level of File Libraries - [C213135]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await dataTable.selectItem(siteName);
expect(await toolbar.isEmpty()).toBe(true, 'toolbar not empty');
beforeEach(async (done) => {
await Utils.pressEscape();
await dataTable.clearSelection();
done();
});
it('selected row is marked with a check circle icon - [C213134]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.selectItem(fileUser);
expect(await dataTable.hasCheckMarkIcon(fileUser)).toBe(true, 'check mark missing');
});
});
describe('Personal Files', () => {
describe('on Personal Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -149,11 +154,10 @@ describe('Toolbar actions - single selection : ', () => {
});
});
describe('File Libraries', () => {
describe('on File Libraries', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.clearSelection();
@@ -193,11 +197,62 @@ describe('Toolbar actions - single selection : ', () => {
});
});
describe('Shared Files', () => {
describe('on a library', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.dataTable.waitForHeader();
await dataTable.clearSelection();
done();
});
it('Available actions when a library is selected - My Libraries - [C213135]', async () => {
await page.goToMyLibraries();
await dataTable.selectItem(siteName);
expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed');
expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${siteName}`);
expect(await toolbar.isButtonPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`);
await toolbar.openMoreMenu();
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`);
expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`);
});
it('Available actions when a library is selected - Favorite Libraries - user is a member - [C289892]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(siteName);
expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed');
expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${siteName}`);
expect(await toolbar.isButtonPresent('Leave library')).toBe(true, `Leave is not displayed for ${siteName}`);
await toolbar.openMoreMenu();
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${siteName}`);
expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${siteName}`);
});
it('Available actions when a library is selected - Favorite Libraries - user is not a member - [C290090]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(adminPublic);
expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed');
expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${adminPublic}`);
expect(await toolbar.isButtonPresent('Join')).toBe(true, `Join is not displayed for ${adminPublic}`);
await toolbar.openMoreMenu();
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminPublic}`);
expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminPublic}`);
});
it('Available actions when a library is selected - Favorite Libraries - user requested to join - [C290091]', async () => {
await page.goToFavoriteLibraries();
await dataTable.selectItem(adminModerated);
expect(await toolbar.isEmpty()).toBe(false, 'toolbar not displayed');
expect(await toolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed for ${adminModerated}`);
expect(await toolbar.isButtonPresent('Cancel join request')).toBe(true, `Cancel join is not displayed for ${adminModerated}`);
await toolbar.openMoreMenu();
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${adminModerated}`);
expect(await toolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed for ${adminModerated}`);
});
});
describe('on Shared Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.clickSharedFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -212,8 +267,8 @@ describe('Toolbar actions - single selection : ', () => {
expect(await toolbar.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`);
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`);
expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`);
expect(await toolbar.isShareEditButtonPresent()).toBe(true, `Shared link settings is not displayed for ${fileUser}`);
await toolbar.openMoreMenu();
expect(await toolbar.menu.isMenuItemPresent('Shared link settings')).toBe(true, `Shared is not displayed for ${fileUser}`);
expect(await toolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`);
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`);
expect(await toolbar.menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`);
@@ -222,11 +277,10 @@ describe('Toolbar actions - single selection : ', () => {
});
});
describe('Recent Files', () => {
describe('on Recent Files', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
await dataTable.clearSelection();
done();
});
@@ -250,11 +304,10 @@ describe('Toolbar actions - single selection : ', () => {
});
});
describe('Favorites', () => {
describe('on Favorites', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.clearSelection();
done();
});
@@ -292,11 +345,10 @@ describe('Toolbar actions - single selection : ', () => {
});
});
describe('Trash', () => {
describe('on Trash', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
await dataTable.clearSelection();
done();
});
+61 -85
View File
@@ -24,8 +24,8 @@
*/
import { browser } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SIDEBAR_LABELS, SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { ShareDialog } from '../../components/dialog/share-dialog';
import { ConfirmDialog } from '../../components/dialog/confirm-dialog';
@@ -43,7 +43,6 @@ describe('Unshare a file', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable, toolbar } = page;
const shareDialog = new ShareDialog();
@@ -59,10 +58,7 @@ describe('Unshare a file', () => {
});
afterAll(async (done) => {
await Promise.all([
apis.user.nodes.deleteNodeById(parentId),
logoutPage.load()
]);
await apis.user.nodes.deleteNodeById(parentId);
done();
});
@@ -88,8 +84,7 @@ describe('Unshare a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(parent);
await dataTable.waitForHeader();
done();
@@ -111,8 +106,7 @@ describe('Unshare a file', () => {
it('Unshare dialog UI - [C286339]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked');
@@ -127,8 +121,7 @@ describe('Unshare a file', () => {
it('Unshare a file - [C286340]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -140,7 +133,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`);
await browser.get(url);
@@ -152,8 +145,7 @@ describe('Unshare a file', () => {
it('Cancel the Unshare action - [C286341]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const urlBefore = await shareDialog.getLinkUrl();
@@ -170,7 +162,7 @@ describe('Unshare a file', () => {
it('Unshare a file from the context menu - [C286359]', async () => {
await dataTable.rightClickOnItem(file4);
await contextMenu.clickMenuItem('Shared link settings');
await contextMenu.clickShareEditAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -182,7 +174,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`);
await browser.get(url);
@@ -222,8 +214,7 @@ describe('Unshare a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(siteName);
await dataTable.waitForHeader();
await dataTable.doubleClickOnRowByName(parentInSite);
@@ -244,8 +235,7 @@ describe('Unshare a file', () => {
it('Unshare dialog UI - [C286679]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked');
@@ -260,8 +250,7 @@ describe('Unshare a file', () => {
it('Unshare a file - [C286680]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -273,7 +262,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`);
await browser.get(url);
@@ -285,8 +274,7 @@ describe('Unshare a file', () => {
it('Cancel the Unshare action - [C286681]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const urlBefore = await shareDialog.getLinkUrl();
@@ -303,7 +291,7 @@ describe('Unshare a file', () => {
it('Unshare a file from the context menu - [C286683]', async () => {
await dataTable.rightClickOnItem(file4);
await contextMenu.clickMenuItem('Shared link settings');
await contextMenu.clickShareEditAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -315,7 +303,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`);
await browser.get(url);
@@ -348,8 +336,7 @@ describe('Unshare a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
@@ -369,8 +356,7 @@ describe('Unshare a file', () => {
it('Unshare dialog UI - [C286689]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked');
@@ -385,8 +371,7 @@ describe('Unshare a file', () => {
it('Unshare a file - [C286690]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -398,7 +383,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`);
await browser.get(url);
@@ -410,8 +395,7 @@ describe('Unshare a file', () => {
it('Cancel the Unshare action - [C286691]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const urlBefore = await shareDialog.getLinkUrl();
@@ -428,7 +412,7 @@ describe('Unshare a file', () => {
it('Unshare a file from the context menu - [C286693]', async () => {
await dataTable.rightClickOnItem(file4);
await contextMenu.clickMenuItem('Shared link settings');
await contextMenu.clickShareEditAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -440,7 +424,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`);
await browser.get(url);
@@ -473,8 +457,7 @@ describe('Unshare a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
done();
});
@@ -494,8 +477,7 @@ describe('Unshare a file', () => {
it('Unshare dialog UI - [C286684]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked');
@@ -510,8 +492,7 @@ describe('Unshare a file', () => {
it('Unshare a file - [C286685]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -534,8 +515,7 @@ describe('Unshare a file', () => {
it('Cancel the Unshare action - [C286686]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
const urlBefore = await shareDialog.getLinkUrl();
@@ -552,7 +532,7 @@ describe('Unshare a file', () => {
it('Unshare a file from the context menu - [C286688]', async () => {
await dataTable.rightClickOnItem(file4);
await contextMenu.clickMenuItem('Shared link settings');
await contextMenu.clickShareEditAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -603,8 +583,7 @@ describe('Unshare a file', () => {
beforeEach(async (done) => {
await page.refresh();
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -624,8 +603,9 @@ describe('Unshare a file', () => {
it('Unshare dialog UI - [C286694]', async () => {
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
// TODO: remove workaround for favorites
// await toolbar.clickShareEditButton();
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked');
@@ -640,8 +620,9 @@ describe('Unshare a file', () => {
it('Unshare a file - [C286695]', async () => {
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
// TODO: remove workaround for favorites
// await toolbar.clickShareEditButton();
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -653,7 +634,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file2Id)).toBe(false, `${file2} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file2).isPresent()).toBe(false, `${file2} is in the Shared files list`);
await browser.get(url);
@@ -665,8 +646,9 @@ describe('Unshare a file', () => {
it('Cancel the Unshare action - [C286696]', async () => {
await dataTable.selectItem(file3);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
// TODO: remove workaround for favorites
// await toolbar.clickShareEditButton();
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
const urlBefore = await shareDialog.getLinkUrl();
@@ -683,7 +665,9 @@ describe('Unshare a file', () => {
it('Unshare a file from the context menu - [C286698]', async () => {
await dataTable.rightClickOnItem(file4);
await contextMenu.clickMenuItem('Share');
// TODO: remove workaround for favorites
// await toolbar.clickShareEditButton();
await contextMenu.clickShareAction();
await shareDialog.waitForDialogToOpen();
const url = await shareDialog.getLinkUrl();
await shareDialog.clickShareToggle();
@@ -695,7 +679,7 @@ describe('Unshare a file', () => {
expect(await apis.user.nodes.isFileShared(file4Id)).toBe(false, `${file4} is shared`);
// TODO: disable check cause api is slow to update
// await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
// await page.clickSharedFiles();
// expect(await dataTable.getRowByName(file4).isPresent()).toBe(false, `${file4} is in the Shared files list`);
await browser.get(url);
@@ -720,7 +704,7 @@ describe('Unshare a file', () => {
file1Id = (await apis.admin.nodes.createFile(file1, docLibId)).entry.id;
file2Id = (await apis.admin.nodes.createFile(file2, docLibId)).entry.id;
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.admin.shared.shareFileById(file1Id);
await apis.user.shared.shareFileById(file2Id);
@@ -749,70 +733,62 @@ describe('Unshare a file', () => {
});
it('on File Libraries - file shared by other user - [C286682]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(sitePrivate);
await dataTable.waitForHeader();
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer');
});
it('on File Libraries - file shared by the user - [C286701]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await dataTable.doubleClickOnRowByName(sitePrivate);
await dataTable.waitForHeader();
await dataTable.selectItem(file2);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer');
});
it('on Shared Files - file shared by other user - [C286687]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer');
});
it('on Shared Files - file shared by the user - [C286702]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Shared link settings');
await toolbar.clickShareEditButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer');
});
it('on Favorites - file shared by other user - [C286697]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
// TODO: remove workaround for favorites
// await toolbar.clickShareEditButton();
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer');
});
it('on Favorites - file shared by the user - [C286703]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
await dataTable.selectItem(file1);
await toolbar.openMoreMenu();
await toolbar.menu.clickMenuItem('Share');
// TODO: remove workaround for favorites
// await toolbar.clickShareEditButton();
await toolbar.clickShareButton();
await shareDialog.waitForDialogToOpen();
expect(await shareDialog.isShareToggleDisabled()).toBe(false, 'Share toggle enabled for consumer');
+3 -10
View File
@@ -23,9 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
// import { browser, protractor, promise } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -40,7 +38,6 @@ describe('Upload files', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable } = page;
@@ -53,16 +50,12 @@ describe('Upload files', () => {
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
afterAll(async (done) => {
await Promise.all([
apis.user.nodes.deleteNodeById(folder1Id),
logoutPage.load()
]);
await apis.user.nodes.deleteNodeById(folder1Id);
done();
});
+11 -7
View File
@@ -24,14 +24,13 @@
*/
import { browser } from 'protractor';
import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages';
import { BrowsingPage, LoginPage } from '../../pages/pages';
import { CreateOrEditFolderDialog } from '../../components/dialog/create-edit-folder-dialog';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
describe('General', () => {
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const createDialog = new CreateOrEditFolderDialog();
const adminApi = new RepoClient();
@@ -48,26 +47,31 @@ describe('General', () => {
afterAll(async (done) => {
await nodesApi.deleteNodeById(folderId);
await logoutPage.load();
done();
});
it('should close opened dialogs - [C286473]', async () => {
await loginPage.loginWithAdmin();
await page.sidenav.openCreateDialog();
await page.sidenav.openCreateFolderDialog();
await createDialog.waitForDialogToOpen();
await createDialog.enterName(folder);
await authApi.logout();
await createDialog.clickCreate();
expect(await browser.getTitle()).toContain('Sign in');
const message = await page.getSnackBarMessage();
expect(message).toEqual('The action was unsuccessful. Try again or contact your IT Team.');
await createDialog.waitForDialogToClose();
expect(createDialog.component.isPresent()).not.toBe(true, 'dialog is present');
expect(await browser.getTitle()).toContain('Sign in');
try {
await createDialog.waitForDialogToClose();
} catch (error) {
console.log('err: ', error);
}
expect(await createDialog.isDialogOpen()).not.toBe(true, 'dialog is present');
});
});
});
+18 -25
View File
@@ -24,25 +24,22 @@
*/
import { browser } from 'protractor';
import { SIDEBAR_LABELS, PAGE_TITLES } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
describe('Page titles', () => {
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const adminApi = new RepoClient();
const { nodes: nodesApi } = adminApi;
const file = `file-${Utils.random()}.txt`; let fileId;
const header = page.header;
const { searchInput } = page.header;
xit('');
describe('on Login / Logout pages', () => {
it('on Login page - [C217155]', async () => {
await loginPage.load();
@@ -71,69 +68,65 @@ describe('Page titles', () => {
});
afterAll(async (done) => {
await Promise.all([
logoutPage.load(),
adminApi.nodes.deleteNodeById(fileId)
]);
await adminApi.nodes.deleteNodeById(fileId);
done();
});
it('Personal Files page - [C217157]', async () => {
const label = SIDEBAR_LABELS.PERSONAL_FILES;
await page.sidenav.navigateToLinkByLabel(label);
await page.sidenav.navigateToLink(label);
expect(await browser.getTitle()).toContain(label);
});
it('File Libraries page - [C217158]', async () => {
const label = SIDEBAR_LABELS.FILE_LIBRARIES;
it('My Libraries page - [C217158]', async () => {
await page.goToMyLibraries();
expect(await browser.getTitle()).toContain(PAGE_TITLES.MY_LIBRARIES);
});
await page.sidenav.navigateToLinkByLabel(label);
expect(await browser.getTitle()).toContain(label);
it('Favorite Libraries page - [C289907]', async () => {
await page.goToFavoriteLibraries();
expect(await browser.getTitle()).toContain(PAGE_TITLES.FAVORITE_LIBRARIES);
});
it('Shared Files page - [C217159]', async () => {
const label = SIDEBAR_LABELS.SHARED_FILES;
await page.sidenav.navigateToLinkByLabel(label);
await page.sidenav.navigateToLink(label);
expect(await browser.getTitle()).toContain(label);
});
it('Recent Files page - [C217160]', async () => {
const label = SIDEBAR_LABELS.RECENT_FILES;
await page.sidenav.navigateToLinkByLabel(label);
await page.sidenav.navigateToLink(label);
expect(await browser.getTitle()).toContain(label);
});
it('Favorites page - [C217161]', async () => {
const label = SIDEBAR_LABELS.FAVORITES;
await page.sidenav.navigateToLinkByLabel(label);
await page.sidenav.navigateToLink(label);
expect(await browser.getTitle()).toContain(label);
});
it('Trash page - [C217162]', async () => {
const label = SIDEBAR_LABELS.TRASH;
await page.sidenav.navigateToLinkByLabel(label);
await page.sidenav.navigateToLink(label);
expect(await browser.getTitle()).toContain(label);
});
it('File Preview page - [C280415]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(file);
expect(await browser.getTitle()).toContain(PAGE_TITLES.VIEWER);
await Utils.pressEscape();
});
it('Search Results page - [C280413]', async () => {
await header.waitForSearchButton();
await header.searchButton.click();
await page.dataTable.waitForHeader();
await header.waitForSearchBar();
await header.searchForText(file);
await searchInput.clickSearchButton();
await searchInput.searchFor(file);
expect(await browser.getTitle()).toContain(PAGE_TITLES.SEARCH);
});
});
+1 -4
View File
@@ -26,7 +26,7 @@
import { browser } from 'protractor';
import { APP_ROUTES } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { navigate } from '../../utilities/browser-utils';
@@ -34,7 +34,6 @@ import { navigate } from '../../utilities/browser-utils';
describe('Login', () => {
const peopleApi = new RepoClient().people;
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
/* cspell:disable-next-line */
const testUser = `user-${Utils.random()}@alfness`;
@@ -70,7 +69,6 @@ describe('Login', () => {
});
afterEach(async (done) => {
await logoutPage.load();
await Utils.clearLocalStorage();
done();
});
@@ -147,7 +145,6 @@ describe('Login', () => {
it('user is able to login after changing his password - [C213104]', async () => {
await loginPage.loginWith(testUser2.username, testUser2.password);
await logoutPage.load();
await peopleApi.changePassword(testUser2.username, newPassword);
await loginPage.loginWith(testUser2.username, newPassword);
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES);
+1 -7
View File
@@ -24,7 +24,7 @@
*/
import { browser } from 'protractor';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { APP_ROUTES } from '../../configs';
@@ -32,7 +32,6 @@ import { APP_ROUTES } from '../../configs';
describe('Logout', () => {
const page = new BrowsingPage();
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const peopleApi = new RepoClient().people;
@@ -48,11 +47,6 @@ describe('Logout', () => {
done();
});
afterEach(async (done) => {
await logoutPage.load();
done();
});
it('Sign out option is available - [C213143]', async () => {
await page.header.userInfo.openMenu();
expect(await page.header.userInfo.menu.isMenuItemPresent('Sign out')).toBe(true, 'Sign out option not displayed');
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { EXTENSIBILITY_CONFIGS, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { EXTENSIBILITY_CONFIGS } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { Utils } from '../../utilities/utils';
@@ -52,9 +52,8 @@ describe('Extensions - Context submenu', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const personalFilesPage = new BrowsingPage();
const {dataTable} = personalFilesPage;
const page = new BrowsingPage();
const {dataTable} = page;
const contextMenu = dataTable.menu;
beforeAll(async (done) => {
@@ -63,7 +62,7 @@ describe('Extensions - Context submenu', () => {
folderId = (await apis.user.nodes.createFolder(folder)).entry.id;
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.CONTEXT_SUBMENUS);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.CONTEXT_SUBMENUS);
await loginPage.loginWith(username);
done();
@@ -72,15 +71,13 @@ describe('Extensions - Context submenu', () => {
beforeEach(async (done) => {
await Utils.pressEscape();
await dataTable.clearSelection();
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
afterAll(async (done) => {
await apis.user.nodes.deleteNodeById(fileId, true);
await apis.user.nodes.deleteNodeById(folderId, true);
await logoutPage.load();
done();
});
@@ -23,9 +23,9 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages';
import { BrowsingPage, LoginPage } from '../../pages/pages';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { EXTENSIBILITY_CONFIGS, SIDEBAR_LABELS } from '../../configs';
import { EXTENSIBILITY_CONFIGS } from '../../configs';
import { Utils } from '../../utilities/utils';
describe('Extensions - DocumentList presets', () => {
@@ -63,31 +63,27 @@ describe('Extensions - DocumentList presets', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const personalFilesPage = new BrowsingPage();
const { dataTable } = personalFilesPage;
const page = new BrowsingPage();
const { dataTable } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
fileId = (await apis.user.nodes.createFile(file)).entry.id;
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.DOCUMENT_LIST_PRESETS);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.DOCUMENT_LIST_PRESETS);
await loginPage.loginWith(username);
done();
});
beforeEach(async done => {
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
afterAll(async (done) => {
await apis.user.nodes.deleteNodeById(fileId);
await logoutPage.load();
done();
});
+2 -8
View File
@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage } from '../../pages/pages';
import { LoginPage } from '../../pages/pages';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { EXTENSIBILITY_CONFIGS } from '../../configs';
import { Utils } from '../../utilities/utils';
@@ -56,12 +56,11 @@ describe('Extensions - Info Drawer', () => {
const toolbarMenu = new Menu();
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.HEADER);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.HEADER);
await loginPage.loginWith(username);
done();
});
@@ -71,11 +70,6 @@ describe('Extensions - Info Drawer', () => {
done();
});
afterAll(async (done) => {
await logoutPage.load();
done();
});
it('Add a new button in the header - [C286474]', async () => {
await header.openMoreMenu();
expect(await toolbarMenu.isMenuItemPresent(enabledMenu.title)).toBe(true, 'menu item not present');
+8 -21
View File
@@ -23,10 +23,10 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { InfoDrawer } from './../../components/info-drawer/info-drawer';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { SIDEBAR_LABELS, EXTENSIBILITY_CONFIGS } from '../../configs';
import { EXTENSIBILITY_CONFIGS } from '../../configs';
import { Utils } from '../../utilities/utils';
describe('Extensions - Info Drawer', () => {
@@ -65,7 +65,6 @@ describe('Extensions - Info Drawer', () => {
const infoDrawer = new InfoDrawer();
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
beforeAll(async (done) => {
@@ -82,23 +81,17 @@ describe('Extensions - Info Drawer', () => {
describe('', () => {
beforeAll(async (done) => {
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.INFO_DRAWER);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.INFO_DRAWER);
await loginPage.loginWith(username);
done();
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.clearSelection();
done();
});
afterAll(async (done) => {
await logoutPage.load();
done();
});
it('Add a new tab with icon and title - [C284646]', async () => {
await page.dataTable.selectItem(file);
await page.toolbar.getButtonByTitleAttribute('View details').click();
@@ -106,7 +99,7 @@ describe('Extensions - Info Drawer', () => {
const val = await infoDrawer.getTabTitle(custom_tab.order);
expect(await infoDrawer.isTabPresent(custom_tab.title)).toBe(true, `${custom_tab.title} tab is not present`);
expect(val).toEqual(`${custom_tab.icon}\n${custom_tab.title}`);
expect(val.trim()).toEqual(`${custom_tab.icon}\n${custom_tab.title}`.trim());
});
it('Remove existing tab - [C284647]', async () => {
@@ -132,7 +125,7 @@ describe('Extensions - Info Drawer', () => {
await infoDrawer.waitForInfoDrawerToOpen();
expect(await infoDrawer.isTabPresent(no_title_tab.title)).toBe(true, `${no_title_tab.title} tab is not present`);
expect(await infoDrawer.getTabTitle(no_title_tab.order)).toEqual(`${no_title_tab.icon}`);
expect((await infoDrawer.getTabTitle(no_title_tab.order)).trim()).toEqual(`${no_title_tab.icon}`.trim());
});
it('Insert new component in tab - [C284651]', async () => {
@@ -149,15 +142,9 @@ describe('Extensions - Info Drawer', () => {
describe('', () => {
beforeAll(async (done) => {
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.INFO_DRAWER_EMPTY);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.INFO_DRAWER_EMPTY);
await loginPage.loginWith(username);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
done();
});
afterAll(async (done) => {
await logoutPage.load();
await page.clickPersonalFilesAndWait();
done();
});
+2 -4
View File
@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { BrowsingPage, LoginPage, LogoutPage } from '../../pages/pages';
import { BrowsingPage, LoginPage } from '../../pages/pages';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { EXTENSIBILITY_CONFIGS } from '../../configs';
import { Utils } from '../../utilities/utils';
@@ -66,7 +66,6 @@ describe('Extensions - Metadata presets', () => {
const metadataCard = new MetadataCard();
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
beforeAll(async done => {
@@ -74,7 +73,7 @@ describe('Extensions - Metadata presets', () => {
fileId = (await apis.user.nodes.createImage(file)).entry.id;
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.METADATA_PRESETS);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.METADATA_PRESETS);
await loginPage.loginWith(username);
done();
@@ -96,7 +95,6 @@ describe('Extensions - Metadata presets', () => {
afterAll(async done => {
await apis.user.nodes.deleteNodeById(fileId);
await logoutPage.load();
done();
});
+4 -7
View File
@@ -23,10 +23,10 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Viewer } from './../../components/components';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { SIDEBAR_LABELS, EXTENSIBILITY_CONFIGS, FILES } from '../../configs';
import { EXTENSIBILITY_CONFIGS, FILES } from '../../configs';
import { Utils } from '../../utilities/utils';
describe('Extensions - Viewer', () => {
@@ -72,7 +72,6 @@ describe('Extensions - Viewer', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const viewer = new Viewer();
@@ -84,20 +83,18 @@ describe('Extensions - Viewer', () => {
docxFileId = (await apis.user.upload.uploadFile(docxFile.file_name)).entry.id;
await loginPage.load();
await Utils.setSessionStorageFromConfig('"aca.extension.config"', EXTENSIBILITY_CONFIGS.VIEWER);
await Utils.setSessionStorageFromConfig(EXTENSIBILITY_CONFIGS.VIEWER);
await loginPage.loginWith(username);
done();
});
afterAll(async (done) => {
await apis.user.nodes.deleteNodesById([ pdfFileId, docxFileId ]);
await logoutPage.load();
done();
});
beforeEach(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
done();
});
+262
View File
@@ -0,0 +1,262 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { RepoClient } from '../../utilities/repo-client/repo-client';
import { InfoDrawer } from './../../components/info-drawer/info-drawer';
import { Utils } from '../../utilities/utils';
describe('Library properties', () => {
const username = `user1-${Utils.random()}`;
const user2 = `user2-${Utils.random()}`;
const user3 = `user3-${Utils.random()}`;
const site = {
name: `site1-${Utils.random()}`,
id: `site-id-${Utils.random()}`,
visibility: SITE_VISIBILITY.MODERATED,
description: 'my site description'
};
const siteForUpdate = {
name: `site2-${Utils.random()}`,
id: `site-id-${Utils.random()}`,
visibility: SITE_VISIBILITY.MODERATED,
description: 'my initial description'
};
const siteUpdated = {
name: `site-for-rename-${Utils.random()}`,
visibility: SITE_VISIBILITY.PRIVATE,
description: 'new description'
}
const siteDup = `site3-${Utils.random()}`;
const apis = {
admin: new RepoClient(),
user: new RepoClient(username, username)
};
const infoDrawer = new InfoDrawer();
const loginPage = new LoginPage();
const page = new BrowsingPage();
const { dataTable } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.people.createUser({ username: user2 });
await apis.admin.people.createUser({ username: user3 });
await apis.user.sites.createSite(site.name, site.visibility, site.description, site.id);
await apis.user.sites.createSite(siteForUpdate.name, siteForUpdate.visibility, siteForUpdate.description, siteForUpdate.id);
await apis.user.sites.createSite(siteDup);
await apis.user.sites.addSiteMember(site.id, user2, SITE_ROLES.SITE_COLLABORATOR.ROLE);
await apis.user.sites.addSiteMember(site.id, user3, SITE_ROLES.SITE_MANAGER.ROLE);
await loginPage.loginWith(username);
done();
});
afterAll(async (done) => {
await apis.user.sites.deleteSite(site.id);
await apis.user.sites.deleteSite(siteForUpdate.id);
await apis.user.sites.deleteSite(siteDup);
done();
});
beforeEach(async (done) => {
await page.clickFileLibrariesAndWait();
done();
});
afterEach(async done => {
if (await infoDrawer.isOpen()) {
await page.toolbar.getButtonByTitleAttribute('View details').click();
}
done();
});
it('Info drawer opens for a library - [C289336]', async () => {
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
expect(await infoDrawer.getHeaderTitle()).toEqual('Details');
expect(await infoDrawer.isTabPresent('About')).toBe(true, 'About tab is not displayed');
expect(await infoDrawer.isNameDisplayed()).toBe(true, 'Name field not displayed');
expect(await infoDrawer.isLibraryIdDisplayed()).toBe(true, 'Library ID field not displayed');
expect(await infoDrawer.isVisibilityDisplayed()).toBe(true, 'Visibility field not displayed');
expect(await infoDrawer.isDescriptionDisplayed()).toBe(true, 'Description field not displayed');
expect(await infoDrawer.getName()).toEqual(site.name);
expect(await infoDrawer.getLibraryId()).toEqual(site.id);
expect((await infoDrawer.getVisibility()).toLowerCase()).toEqual((site.visibility).toLowerCase());
expect(await infoDrawer.getDescription()).toEqual(site.description);
expect(await infoDrawer.isButtonDisplayed('Edit')).toBe(true, 'Edit action is not displayed');
});
it('Editable properties - [C289338]', async () => {
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
expect(await infoDrawer.isButtonEnabled('Edit')).toBe(true, 'Edit action is not enabled');
await infoDrawer.clickButton('Edit');
expect(await infoDrawer.isNameEnabled()).toBe(true, 'Name field not enabled');
expect(await infoDrawer.isLibraryIdEnabled()).toBe(false, 'Library ID field not disabled');
expect(await infoDrawer.isVisibilityEnabled()).toBe(true, 'Visibility field not enabled');
expect(await infoDrawer.isDescriptionEnabled()).toBe(true, 'Description field not enabled');
expect(await infoDrawer.isButtonDisplayed('Cancel')).toBe(true, 'Cancel button not displayed');
expect(await infoDrawer.isButtonDisplayed('Update')).toBe(true, 'Update button not displayed');
expect(await infoDrawer.isButtonEnabled('Cancel')).toBe(true, 'Cancel button not enabled');
expect(await infoDrawer.isButtonEnabled('Update')).toBe(false, 'Update button not disabled');
});
it('Edit site details - [C289339]', async () => {
await dataTable.selectItem(siteForUpdate.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
expect(await infoDrawer.isButtonEnabled('Edit')).toBe(true, 'Edit action is not enabled');
await infoDrawer.clickButton('Edit');
await infoDrawer.enterName(siteUpdated.name);
await infoDrawer.enterDescription(siteUpdated.description);
await infoDrawer.setVisibility(siteUpdated.visibility);
expect(await infoDrawer.isButtonEnabled('Update')).toBe(true, 'Update button not enabled');
await infoDrawer.clickButton('Update');
expect(await page.getSnackBarMessage()).toEqual('Library properties updated');
expect(await dataTable.getRowByName(siteUpdated.name).isPresent()).toBe(true, 'New site name not displayed in the list');
expect(await infoDrawer.isOpen()).toBe(false, 'Info drawer still open');
expect((await apis.user.sites.getSite(siteForUpdate.id)).entry.title).toEqual(siteUpdated.name);
expect((await apis.user.sites.getSite(siteForUpdate.id)).entry.description).toEqual(siteUpdated.description);
expect((await apis.user.sites.getSite(siteForUpdate.id)).entry.visibility).toEqual(siteUpdated.visibility);
});
it('Cancel editing a site - [C289340]', async () => {
const newName = `new-name-${Utils.random}`;
const newDesc = `new desc ${Utils.random}`;
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
expect(await infoDrawer.isButtonEnabled('Edit')).toBe(true, 'Edit action is not enabled');
await infoDrawer.clickButton('Edit');
await infoDrawer.enterName(newName);
await infoDrawer.enterDescription(newDesc);
await infoDrawer.setVisibility(SITE_VISIBILITY.MODERATED);
await infoDrawer.clickButton('Cancel');
expect(await dataTable.getRowByName(newName).isPresent()).toBe(false, 'New site name is displayed in the list');
expect(await dataTable.getRowByName(site.name).isPresent()).toBe(true, 'Original site name not displayed in the list');
expect(await infoDrawer.isOpen()).toBe(true, 'Info drawer not open');
});
it('Warning appears when editing the name of the library by entering an existing name - [C289341]', async () => {
await apis.user.queries.waitForApi(site.name, { expect: 1 });
await dataTable.selectItem(siteDup);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
await infoDrawer.clickButton('Edit');
await infoDrawer.enterName(site.name);
expect(await infoDrawer.isMessageDisplayed()).toBe(true, 'Message not displayed');
expect(await infoDrawer.getMessage()).toEqual('Library name already in use');
});
it('Site name too long - [C289342]', async () => {
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
await infoDrawer.clickButton('Edit');
await infoDrawer.enterName(Utils.string257);
await Utils.pressTab();
expect(await infoDrawer.isErrorDisplayed()).toBe(true, 'Message not displayed');
expect(await infoDrawer.getError()).toEqual('Use 256 characters or less for title');
expect(await infoDrawer.isButtonEnabled('Update')).toBe(false, 'Update button not disabled');
});
it('Site description too long - [C289343]', async () => {
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
await infoDrawer.clickButton('Edit');
await infoDrawer.enterDescription(Utils.string513);
await Utils.pressTab();
expect(await infoDrawer.isErrorDisplayed()).toBe(true, 'Message not displayed');
expect(await infoDrawer.getError()).toEqual('Use 512 characters or less for description');
expect(await infoDrawer.isButtonEnabled('Update')).toBe(false, 'Update button not disabled');
});
describe('Non manager', () => {
afterAll(async done => {
await loginPage.loginWith(username);
done();
});
it('Edit button is not displayed when user is not the library manager - [C289337]', async () => {
await loginPage.loginWith(user2);
await page.clickFileLibrariesAndWait();
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
expect(await infoDrawer.isButtonDisplayed('Edit')).toBe(false, 'Edit action is displayed');
});
it('Error notification - [C289344]', async () => {
await loginPage.loginWith(user3);
await page.clickFileLibrariesAndWait();
await dataTable.selectItem(site.name);
await page.toolbar.getButtonByTitleAttribute('View details').click();
await infoDrawer.waitForInfoDrawerToOpen();
await infoDrawer.clickButton('Edit');
await apis.user.sites.updateSiteMember(site.id, user3, SITE_ROLES.SITE_CONSUMER.ROLE);
await infoDrawer.enterDescription('new description');
await infoDrawer.clickButton('Update');
expect(await page.getSnackBarMessage()).toEqual('There was an error updating library properties');
});
});
});
+93 -17
View File
@@ -23,8 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -38,9 +37,9 @@ describe('Empty list views', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable } = page;
const { dataTable, pagination } = page;
const { searchInput } = page.header;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
@@ -48,50 +47,127 @@ describe('Empty list views', () => {
done();
});
afterAll(async (done) => {
await logoutPage.load();
done();
});
it('empty Personal Files - [C280131]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.clickPersonalFiles();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyDragAndDropText()).toContain('Drag and drop');
});
it('empty File Libraries - [C217099]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
it('empty My Libraries - [C217099]', async () => {
await page.goToMyLibraries();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyStateTitle()).toContain(`You aren't a member of any File Libraries yet`);
expect(await dataTable.getEmptyStateSubtitle()).toContain('Join libraries to upload, view, and share files.');
});
it('empty Favorite Libraries - [C289911]', async () => {
await page.goToFavoriteLibraries();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyStateTitle()).toContain(`No Favorite Libraries`);
expect(await dataTable.getEmptyStateSubtitle()).toContain('Favorite a library that you want to find easily later.');
});
it('empty Shared Files - [C280132]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.clickSharedFiles();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyStateTitle()).toContain('No shared files or folders');
expect(await dataTable.getEmptyStateSubtitle()).toContain('Items you share using the Share option are shown here.');
});
it('empty Recent Files - [C213169]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES)
await page.clickRecentFiles();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyStateTitle()).toContain('No recent files');
expect(await dataTable.getEmptyStateSubtitle()).toContain('Items you upload or edit in the last 30 days are shown here.');
expect(await dataTable.getEmptyStateSubtitle()).toContain('Items you uploaded or edited in the last 30 days are shown here.');
});
it('empty Favorites - [C280133]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await page.clickFavorites();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyStateTitle()).toContain('No favorite files or folders');
expect(await dataTable.getEmptyStateSubtitle()).toContain('Favorite items that you want to easily find later.');
});
it('empty Trash - [C280134]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await dataTable.isEmptyList()).toBe(true, 'list is not empty');
expect(await dataTable.getEmptyStateTitle()).toContain('Trash is empty');
expect(await dataTable.getEmptyStateText()).toContain('Items you delete are moved to the Trash.');
expect(await dataTable.getEmptyStateText()).toContain('Empty Trash to permanently delete items.');
});
it('Favorites - pagination controls not displayed - [C280111]', async () => {
await page.clickFavorites();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('File Libraries - pagination controls not displayed - [C280084]', async () => {
await page.clickFileLibraries();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Personal Files - pagination controls not displayed - [C280075]', async () => {
await page.clickPersonalFiles();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Recent Files - pagination controls not displayed - [C280102]', async () => {
await page.clickRecentFiles();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Shared Files - pagination controls not displayed - [C280094]', async () => {
await page.clickSharedFiles();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Trash - pagination controls not displayed - [C280120]', async () => {
await page.clickTrash();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Search results - pagination controls not displayed - [C290123]', async () => {
await searchInput.clickSearchButton();
await searchInput.checkOnlyFiles();
/* cspell:disable-next-line */
await searchInput.searchFor('qwertyuiop');
await dataTable.waitForBody();
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
});
+8 -11
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -45,16 +45,15 @@ describe('Favorites', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const favoritesPage = new BrowsingPage();
const { dataTable, breadcrumb } = favoritesPage;
const page = new BrowsingPage();
const { dataTable, breadcrumb } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC);
const docLibId = await apis.admin.sites.getDocLibId(siteName);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER.ROLE);
const file1Id = (await apis.admin.nodes.createFile(fileName1, docLibId)).entry.id;
const folderId = (await apis.user.nodes.createFolder(favFolderName)).entry.id;
@@ -77,16 +76,14 @@ describe('Favorites', () => {
});
beforeEach(async (done) => {
await favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
afterAll(async (done) => {
await apis.admin.sites.deleteSite(siteName);
await apis.user.nodes.deleteNodes([ favFolderName, parentFolder ]);
await apis.admin.trashcan.emptyTrash();
await logoutPage.load();
await apis.user.trashcan.emptyTrash();
done();
});
@@ -138,7 +135,7 @@ describe('Favorites', () => {
it('Location column redirect - file in site - [C280485]', async () => {
await dataTable.clickItemLocation(fileName1);
expect(breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName ]);
expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName ]);
});
it('Navigate into folder from Favorites - [C213230]', async () => {
+205 -72
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -32,13 +32,21 @@ 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 userSitePrivate = `user-private-${Utils.random()}`;
const userSiteModerated = `user-moderated-${Utils.random()}`;
const userSitePublic = `user-public-${Utils.random()}`;
const siteName = `siteName-${Utils.random()}`;
const siteId1 = Utils.random();
const siteId2 = Utils.random();
const adminSite = `admin-${Utils.random()}`;
const adminSite1 = `admin1-${Utils.random()}`;
const adminSite2 = `admin2-${Utils.random()}`;
const adminSite3 = `admin3-${Utils.random()}`;
const adminSite4 = `admin4-${Utils.random()}`;
const adminSite5 = `admin5-${Utils.random()}`;
const adminSite6 = `admin6-${Utils.random()}`;
const siteDescription = 'my site description';
@@ -48,94 +56,219 @@ describe('File Libraries', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const fileLibrariesPage = new BrowsingPage();
const { dataTable } = fileLibrariesPage;
const page = new BrowsingPage();
const { dataTable } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(sitePublic, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(siteModerated, SITE_VISIBILITY.MODERATED, siteDescription);
await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE, null);
await apis.admin.sites.createSite(adminSite, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId1);
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId2);
await apis.admin.sites.addSiteMember(sitePublic, username, SITE_ROLES.SITE_CONSUMER);
await apis.admin.sites.addSiteMember(siteModerated, username, SITE_ROLES.SITE_MANAGER);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_CONTRIBUTOR);
await apis.admin.sites.addSiteMember(siteId1, username, SITE_ROLES.SITE_CONTRIBUTOR);
await apis.admin.sites.addSiteMember(siteId2, username, SITE_ROLES.SITE_CONTRIBUTOR);
await apis.user.sites.createSite(userSitePublic, SITE_VISIBILITY.PUBLIC);
await apis.user.sites.createSite(userSiteModerated, SITE_VISIBILITY.MODERATED, siteDescription);
await apis.user.sites.createSite(userSitePrivate, SITE_VISIBILITY.PRIVATE, null);
await apis.admin.sites.createSite(adminSite1, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(adminSite2, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(adminSite3, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(adminSite4, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(adminSite5, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.createSite(adminSite6, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.addSiteMember(adminSite1, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.admin.sites.addSiteMember(adminSite2, username, SITE_ROLES.SITE_CONTRIBUTOR.ROLE);
await apis.admin.sites.addSiteMember(adminSite3, username, SITE_ROLES.SITE_COLLABORATOR.ROLE);
await apis.admin.sites.addSiteMember(adminSite4, username, SITE_ROLES.SITE_MANAGER.ROLE);
await apis.admin.sites.addSiteMember(adminSite6, username, SITE_ROLES.SITE_CONSUMER.ROLE);
await apis.user.favorites.addFavoriteById('site', adminSite1);
await apis.user.favorites.addFavoriteById('site', adminSite2);
await apis.user.favorites.addFavoriteById('site', adminSite3);
await apis.user.favorites.addFavoriteById('site', adminSite4);
await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId1);
await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC, null, siteId2);
await loginPage.loginWith(username);
done();
});
beforeEach(async (done) => {
await fileLibrariesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await dataTable.waitForHeader();
done();
});
afterAll(async (done) => {
await apis.admin.sites.deleteSites([ sitePublic, siteModerated, sitePrivate, adminSite, siteId1, siteId2 ]);
await logoutPage.load();
await apis.user.sites.deleteSites([ userSitePublic, userSiteModerated, userSitePrivate, siteId1, siteId2 ]);
await apis.admin.sites.deleteSites([ adminSite1, adminSite2, adminSite3, adminSite4, adminSite5, adminSite6 ]);
done();
});
it('has the correct columns - [C217095]', async () => {
const labels = [ 'Title', 'Status' ];
const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label));
xit('');
expect(await dataTable.getColumnHeaders().count()).toBe(2 + 1, 'Incorrect number of columns');
describe('My Libraries', () => {
beforeEach(async (done) => {
await page.goToMyLibraries();
done();
});
await elements.forEach(async (element, index) => {
expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`);
it('has the correct columns - [C217095]', async () => {
const labels = [ 'Name', 'My Role', 'Visibility' ];
const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label));
expect(await dataTable.getColumnHeaders().count()).toBe(3 + 1, 'Incorrect number of columns');
await elements.forEach(async (element, index) => {
expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`);
});
});
it('User can see only the sites he is a member of - [C280501]', async () => {
const sitesCount = await dataTable.countRows();
expect(sitesCount).toEqual(10, 'Incorrect number of sites displayed');
expect(await dataTable.getRowByName(adminSite5).isPresent()).toBe(false, `${adminSite5} should not appear in the list`);
});
it('Library visibility is correctly displayed - [C289905]', async () => {
const expectedSitesVisibility = {
[userSitePrivate]: SITE_VISIBILITY.PRIVATE,
[userSiteModerated]: SITE_VISIBILITY.MODERATED,
[userSitePublic]: SITE_VISIBILITY.PUBLIC
};
const rowCells = await dataTable.getRows().map((row) => {
return row.all(dataTable.cell).map(async cell => await cell.getText());
});
const sitesList = rowCells.reduce((acc, cell) => {
acc[cell[1]] = cell[3].toUpperCase();
return acc;
}, {});
Object.keys(expectedSitesVisibility).forEach((site) => {
expect(sitesList[site]).toEqual(expectedSitesVisibility[site]);
});
});
it('User role is correctly displayed - [C289903]', async () => {
const expectedSitesRoles = {
[adminSite1]: SITE_ROLES.SITE_CONSUMER.LABEL,
[adminSite2]: SITE_ROLES.SITE_CONTRIBUTOR.LABEL,
[adminSite3]: SITE_ROLES.SITE_COLLABORATOR.LABEL,
[adminSite4]: SITE_ROLES.SITE_MANAGER.LABEL
};
const rowCells = await dataTable.getRows().map((row) => {
return row.all(dataTable.cell).map(async cell => await cell.getText());
});
const sitesList = rowCells.reduce((acc, cell) => {
acc[cell[1]] = cell[2];
return acc;
}, {});
Object.keys(expectedSitesRoles).forEach((site) => {
expect(sitesList[site]).toEqual(expectedSitesRoles[site]);
});
});
it('Site ID is displayed when two sites have the same name - [C217098]', async () => {
const expectedSites = [
`${siteName} (${siteId1})`,
`${siteName} (${siteId2})`
];
const cells = await dataTable.getCellsContainingName(siteName);
const expectedJSON = JSON.stringify(expectedSites.sort());
const actualJSON = JSON.stringify(cells.sort());
expect(actualJSON).toEqual(expectedJSON);
});
it('Tooltip for sites without description - [C217096]', async () => {
const tooltip = await dataTable.getItemNameTooltip(userSitePrivate);
expect(tooltip).toBe(`${userSitePrivate}`);
});
it('Tooltip for sites with description - [C217097]', async () => {
const tooltip = await dataTable.getItemNameTooltip(userSiteModerated);
expect(tooltip).toBe(`${siteDescription}`);
});
});
it('User can see only the sites he is a member of - [C280501]', async () => {
const sitesCount = await dataTable.countRows();
const expectedSites = {
[sitePrivate]: SITE_VISIBILITY.PRIVATE,
[siteModerated]: SITE_VISIBILITY.MODERATED,
[sitePublic]: SITE_VISIBILITY.PUBLIC
};
expect(sitesCount).toEqual(5, 'Incorrect number of sites displayed');
expect(await dataTable.getRowByName(adminSite).isPresent()).toBe(false, 'Incorrect site appears in list');
const rowCells = await dataTable.getRows().map((row) => {
return row.all(dataTable.cell).map(async cell => await cell.getText());
describe('Favorite Libraries', () => {
beforeEach(async (done) => {
await page.goToFavoriteLibraries();
done();
});
const sitesList = rowCells.reduce((acc, cell) => {
acc[cell[1]] = cell[2].toUpperCase();
return acc;
}, {});
Object.keys(expectedSites).forEach((site) => {
expect(sitesList[site]).toEqual(expectedSites[site]);
it('has the correct columns - [C289893]', async () => {
const labels = [ 'Name', 'My Role', 'Visibility' ];
const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label));
expect(await dataTable.getColumnHeaders().count()).toBe(3 + 1, 'Incorrect number of columns');
await elements.forEach(async (element, index) => {
expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`);
});
});
});
it('Site ID is displayed when two sites have the same name - [C217098]', async () => {
const expectedSites = [
`${siteName} (${siteId1})`,
`${siteName} (${siteId2})`
];
const cells = await dataTable.getCellsContainingName(siteName);
const expectedJSON = JSON.stringify(expectedSites.sort());
const actualJSON = JSON.stringify(cells.sort());
expect(actualJSON).toEqual(expectedJSON);
});
it('User can see only his favorite sites - [C289897]', async () => {
const sitesCount = await dataTable.countRows();
it('Tooltip for sites without description - [C217096]', async () => {
const tooltip = await dataTable.getItemNameTooltip(sitePrivate);
expect(tooltip).toBe(`${sitePrivate}`);
});
expect(sitesCount).toEqual(9, 'Incorrect number of sites displayed');
expect(await dataTable.getRowByName(adminSite6).isPresent()).toBe(false, `${adminSite6} should not appear`);
});
it('Tooltip for sites with description - [C217097]', async () => {
const tooltip = await dataTable.getItemNameTooltip(siteModerated);
expect(tooltip).toBe(`${siteDescription}`);
it('Library visibility is correctly displayed - [C289906]', async () => {
const expectedSitesVisibility = {
[userSitePrivate]: SITE_VISIBILITY.PRIVATE,
[userSiteModerated]: SITE_VISIBILITY.MODERATED,
[userSitePublic]: SITE_VISIBILITY.PUBLIC
};
const rowCells = await dataTable.getRows().map((row) => {
return row.all(dataTable.cell).map(async cell => await cell.getText());
});
const sitesList = rowCells.reduce((acc, cell) => {
acc[cell[1]] = cell[3].toUpperCase();
return acc;
}, {});
Object.keys(expectedSitesVisibility).forEach((site) => {
expect(sitesList[site]).toEqual(expectedSitesVisibility[site]);
});
});
it('User role is correctly displayed - [C289904]', async () => {
const expectedSitesRoles = {
[adminSite1]: SITE_ROLES.SITE_CONSUMER.LABEL,
[adminSite2]: SITE_ROLES.SITE_CONTRIBUTOR.LABEL,
[adminSite3]: SITE_ROLES.SITE_COLLABORATOR.LABEL,
[adminSite4]: SITE_ROLES.SITE_MANAGER.LABEL
};
const rowCells = await dataTable.getRows().map((row) => {
return row.all(dataTable.cell).map(async cell => await cell.getText());
});
const sitesList = rowCells.reduce((acc, cell) => {
acc[cell[1]] = cell[2];
return acc;
}, {});
Object.keys(expectedSitesRoles).forEach((site) => {
expect(sitesList[site]).toEqual(expectedSitesRoles[site]);
});
});
it('Site ID is displayed when two sites have the same name - [C289896]', async () => {
const expectedSites = [
`${siteName} (${siteId1})`,
`${siteName} (${siteId2})`
];
const cells = await dataTable.getCellsContainingName(siteName);
const expectedJSON = JSON.stringify(expectedSites.sort());
const actualJSON = JSON.stringify(cells.sort());
expect(actualJSON).toEqual(expectedJSON);
});
it('Tooltip for sites without description - [C289894]', async () => {
const tooltip = await dataTable.getItemNameTooltip(userSitePrivate);
expect(tooltip).toBe(`${userSitePrivate}`);
});
it('Tooltip for sites with description - [C289895]', async () => {
const tooltip = await dataTable.getItemNameTooltip(userSiteModerated);
expect(tooltip).toBe(`${siteDescription}`);
});
});
});
+6 -13
View File
@@ -24,8 +24,7 @@
*/
import { browser } from 'protractor';
import { SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -44,7 +43,6 @@ describe('Generic errors', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable } = page;
@@ -62,13 +60,11 @@ describe('Generic errors', () => {
afterAll(async (done) => {
await apis.user.nodes.deleteNodeById(parentId);
await apis.user.trashcan.emptyTrash();
await logoutPage.load();
done();
});
it('File / folder not found - [C217313]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(parent);
await dataTable.doubleClickOnRowByName(file1);
const URL = await browser.getCurrentUrl();
@@ -76,31 +72,28 @@ describe('Generic errors', () => {
await browser.get(URL);
expect(await page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed');
expect(await page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`);
expect(await page.getGenericErrorTitle()).toContain(`This item no longer exists or you don't have permission to view it.`);
});
it('Invalid URL - [C217315]', async () => {
await page.load('/invalid page');
expect(await page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed');
expect(await page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`);
expect(await page.getGenericErrorTitle()).toContain(`This item no longer exists or you don't have permission to view it.`);
});
it('Permission denied - [C217314]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(parent);
await dataTable.doubleClickOnRowByName(file2);
const URL = await browser.getCurrentUrl();
await logoutPage.load();
await loginPage.loginWith(username2);
await browser.get(URL);
expect(await page.isGenericErrorDisplayed()).toBe(true, 'Generic error page not displayed');
expect(await page.getGenericErrorTitle()).toContain(`This file or folder no longer exists or you don't have permission to view it.`);
expect(await page.getGenericErrorTitle()).toContain(`This item no longer exists or you don't have permission to view it.`);
await logoutPage.load();
await loginPage.loginWith(username);
});
});
+21 -36
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -38,11 +38,8 @@ describe('Special permissions', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const recentFilesPage = new BrowsingPage();
const favoritesPage = new BrowsingPage();
const sharedPage = new BrowsingPage();
const { dataTable } = recentFilesPage;
const page = new BrowsingPage();
const { dataTable } = page;
xit('');
@@ -58,7 +55,7 @@ describe('Special permissions', () => {
beforeAll(async (done) => {
await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE);
const docLibId = await apis.admin.sites.getDocLibId(sitePrivate);
fileId = (await apis.admin.nodes.createFile(fileName, docLibId)).entry.id;
await apis.user.favorites.addFavoriteById('file', fileId);
@@ -73,42 +70,36 @@ describe('Special permissions', () => {
});
afterEach(async (done) => {
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE);
done();
});
afterAll(async (done) => {
await Promise.all([
apis.admin.sites.deleteSite(sitePrivate),
logoutPage.load()
]);
await apis.admin.sites.deleteSite(sitePrivate);
done();
});
it('on Recent Files - [C213173]', async () => {
await recentFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items');
await apis.admin.sites.deleteSiteMember(sitePrivate, username);
await recentFilesPage.refresh();
await page.refresh();
expect(await dataTable.countRows()).toBe(0, 'Incorrect number of items');
});
it('on Favorites - [C213227]', async () => {
await favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items');
await apis.admin.sites.deleteSiteMember(sitePrivate, username);
await favoritesPage.refresh();
await page.refresh();
expect(await dataTable.countRows()).toBe(0, 'Incorrect number of items');
});
it('on Shared Files - [C213116]', async () => {
await sharedPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items');
await apis.admin.sites.deleteSiteMember(sitePrivate, username);
await sharedPage.refresh();
await page.refresh();
expect(await dataTable.countRows()).toBe(0, 'Incorrect number of items');
});
});
@@ -120,7 +111,7 @@ describe('Special permissions', () => {
beforeAll(async (done) => {
await apis.admin.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR);
await apis.admin.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE);
const docLibId = await apis.admin.sites.getDocLibId(sitePrivate);
fileId = (await apis.user.nodes.createFile(fileName, docLibId)).entry.id;
await apis.user.favorites.addFavoriteById('file', fileId);
@@ -133,32 +124,26 @@ describe('Special permissions', () => {
});
afterAll(async (done) => {
await Promise.all([
apis.admin.sites.deleteSite(sitePrivate),
logoutPage.load()
]);
await apis.admin.sites.deleteSite(sitePrivate);
done();
});
it(`on Recent Files - [C213178]`, async () => {
await recentFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items');
expect(await dataTable.getItemLocation(fileName)).toEqual('');
expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown');
});
it(`on Favorites - [C213672]`, async () => {
await favoritesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items');
expect(await dataTable.getItemLocation(fileName)).toEqual('');
expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown');
});
it(`on Shared Files - [C213668]`, async () => {
await sharedPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
expect(await dataTable.countRows()).toBe(1, 'Incorrect number of items');
expect(await dataTable.getItemLocation(fileName)).toEqual('');
expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown');
});
});
});
+13 -26
View File
@@ -25,8 +25,8 @@
import { browser } from 'protractor';
import { SIDEBAR_LABELS, APP_ROUTES } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { APP_ROUTES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -39,9 +39,8 @@ describe('Personal Files', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const personalFilesPage = new BrowsingPage();
const { dataTable } = personalFilesPage;
const page = new BrowsingPage();
const { dataTable } = page;
const adminFolder = `admin-folder-${Utils.random()}`;
@@ -75,19 +74,13 @@ describe('Personal Files', () => {
});
beforeEach(async (done) => {
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
done();
});
afterAll(async (done) => {
await logoutPage.load();
await page.clickPersonalFilesAndWait();
done();
});
it('has Data Dictionary and created content - [C213241]', async () => {
expect(await dataTable.getRowByName('Data Dictionary').isPresent()).toBe(true);
expect(await dataTable.getRowByName(adminFolder).isPresent()).toBe(true);
expect(await dataTable.getRowByName('Data Dictionary').isPresent()).toBe(true, 'Data Dictionary not displayed');
expect(await dataTable.getRowByName(adminFolder).isPresent()).toBe(true, 'admin folder not displayed');
});
});
@@ -98,13 +91,7 @@ describe('Personal Files', () => {
});
beforeEach(async (done) => {
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await dataTable.waitForHeader();
done();
});
afterAll(async (done) => {
await logoutPage.load();
await page.clickPersonalFilesAndWait();
done();
});
@@ -124,7 +111,7 @@ describe('Personal Files', () => {
});
it('has user created content - [C213242]', async () => {
expect(await dataTable.getRowByName(userFolder).isPresent()).toBe(true);
expect(await dataTable.getRowByName(userFolder).isPresent()).toBe(true, 'user folder not displayed');
});
it('navigates to folder - [C213244]', async () => {
@@ -138,20 +125,20 @@ describe('Personal Files', () => {
});
it('redirects to Personal Files on clicking the link from sidebar - [C213245]', async () => {
await personalFilesPage.dataTable.doubleClickOnRowByName(userFolder);
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.doubleClickOnRowByName(userFolder);
await page.clickPersonalFiles();
const url = await browser.getCurrentUrl();
expect(url.endsWith(APP_ROUTES.PERSONAL_FILES)).toBe(true, 'incorrect url');
});
it('page loads correctly after browser refresh - [C213246]', async () => {
await personalFilesPage.refresh();
await page.refresh();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES);
});
it('page load by URL - [C213247]', async () => {
const url = await browser.getCurrentUrl();
await personalFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
await browser.get(url);
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES);
});
+7 -10
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -46,9 +46,8 @@ describe('Recent Files', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const recentFilesPage = new BrowsingPage();
const { dataTable, breadcrumb } = recentFilesPage;
const page = new BrowsingPage();
const { dataTable, breadcrumb } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
@@ -70,16 +69,14 @@ describe('Recent Files', () => {
});
beforeEach(async (done) => {
await recentFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await dataTable.waitForHeader();
await page.clickRecentFilesAndWait();
done();
});
afterAll(async (done) => {
await apis.user.nodes.deleteNodesById([ folderId, file2Id ]);
await apis.user.sites.deleteSite(siteName);
await apis.admin.trashcan.emptyTrash();
await logoutPage.load();
await apis.user.trashcan.emptyTrash();
done();
});
@@ -134,6 +131,6 @@ describe('Recent Files', () => {
it('Location column redirect - file in site - [C280487]', async () => {
await dataTable.clickItemLocation(fileSite);
expect(await breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName, folderSite ]);
expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName, folderSite ]);
});
});
+7 -10
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -47,14 +47,13 @@ describe('Shared Files', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const sharedFilesPage = new BrowsingPage();
const { dataTable, breadcrumb } = sharedFilesPage;
const page = new BrowsingPage();
const { dataTable, breadcrumb } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_CONSUMER.ROLE);
const docLibId = await apis.admin.sites.getDocLibId(siteName);
const nodeId = (await apis.admin.nodes.createFile(fileAdmin, docLibId)).entry.id;
await apis.admin.shared.shareFileById(nodeId);
@@ -76,8 +75,7 @@ describe('Shared Files', () => {
});
beforeEach(async (done) => {
await sharedFilesPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await dataTable.waitForHeader();
await page.clickSharedFilesAndWait();
done();
});
@@ -85,7 +83,6 @@ describe('Shared Files', () => {
await apis.admin.sites.deleteSite(siteName);
await apis.user.nodes.deleteNodeById(folderId);
await apis.user.nodes.deleteNodeById(file4Id);
await logoutPage.load();
done();
});
@@ -134,7 +131,7 @@ describe('Shared Files', () => {
it('Location column redirect - file in site - [C280491]', async () => {
await dataTable.clickItemLocation(fileAdmin);
expect(await breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName ]);
expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName ]);
});
it('Location column displays a tooltip with the entire path of the file - [C213667]', async () => {
+7 -12
View File
@@ -23,8 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -52,7 +51,6 @@ describe('File / folder tooltips', () => {
const fileDescription = 'file description';
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { dataTable } = page;
@@ -82,8 +80,7 @@ describe('File / folder tooltips', () => {
afterAll(async (done) => {
await Promise.all([
apis.user.nodes.deleteNodes([ parent ]),
apis.user.trashcan.emptyTrash(),
logoutPage.load()
apis.user.trashcan.emptyTrash()
]);
done();
});
@@ -92,7 +89,7 @@ describe('File / folder tooltips', () => {
describe('on Personal Files', () => {
beforeAll(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(parent);
done();
});
@@ -133,7 +130,7 @@ describe('File / folder tooltips', () => {
describe('on Recent Files', () => {
beforeAll(async (done) => {
await apis.user.search.waitForApi(username, { expect: 8 });
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await page.clickRecentFilesAndWait();
done();
});
@@ -174,7 +171,7 @@ describe('File / folder tooltips', () => {
xdescribe('on Shared Files', () => {
beforeAll(async (done) => {
await apis.user.shared.waitForApi({ expect: 8 });
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.clickSharedFilesAndWait();
done();
});
@@ -213,8 +210,7 @@ describe('File / folder tooltips', () => {
describe('on Favorites', () => {
beforeAll(async (done) => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await dataTable.waitForHeader();
await page.clickFavoritesAndWait();
done();
});
@@ -271,8 +267,7 @@ describe('File / folder tooltips', () => {
file1TrashId, file2TrashId, file3TrashId, file4TrashId, file5TrashId, file6TrashId, file7TrashId, file8TrashId
], false);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
await page.clickTrashAndWait();
done();
});
+8 -21
View File
@@ -23,8 +23,8 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY, SITE_ROLES } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -52,16 +52,15 @@ describe('Trash', () => {
};
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const trashPage = new BrowsingPage();
const { dataTable, breadcrumb } = trashPage;
const page = new BrowsingPage();
const { dataTable, breadcrumb } = page;
beforeAll(async (done) => {
await apis.admin.people.createUser({ username });
fileAdminId = (await apis.admin.nodes.createFiles([ fileAdmin ])).entry.id;
folderAdminId = (await apis.admin.nodes.createFolders([ folderAdmin ])).entry.id;
await apis.admin.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER);
await apis.admin.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER.ROLE);
const docLibId = await apis.admin.sites.getDocLibId(siteName);
fileSiteId = (await apis.admin.nodes.createFile(fileSite, docLibId)).entry.id;
fileUserId = (await apis.user.nodes.createFiles([ fileUser ])).entry.id;
@@ -97,13 +96,7 @@ describe('Trash', () => {
});
beforeEach(async (done) => {
await trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
done();
});
afterAll(async (done) => {
await logoutPage.load();
await page.clickTrashAndWait();
done();
});
@@ -136,13 +129,7 @@ describe('Trash', () => {
});
beforeEach(async (done) => {
await trashPage.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await dataTable.waitForHeader();
done();
});
afterAll(async (done) => {
await logoutPage.load();
await page.clickTrashAndWait();
done();
});
@@ -199,7 +186,7 @@ describe('Trash', () => {
it('Location column redirect - file in site - [C280497]', async () => {
await dataTable.clickItemLocation(fileSite);
expect(await breadcrumb.getAllItems()).toEqual([ 'File Libraries', siteName ]);
expect(await breadcrumb.getAllItems()).toEqual([ 'My Libraries', siteName ]);
});
});
});
+26 -32
View File
@@ -25,8 +25,8 @@
import { browser } from 'protractor';
import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { SITE_VISIBILITY } from '../../configs';
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
@@ -45,7 +45,6 @@ describe('Breadcrumb', () => {
const folder1Renamed = `renamed-${Utils.random()}`;
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { breadcrumb } = page;
@@ -79,51 +78,55 @@ describe('Breadcrumb', () => {
await Promise.all([
apis.user.nodes.deleteNodeById(parentId),
apis.user.nodes.deleteNodeById(parent2Id),
apis.user.sites.deleteSite(siteName),
logoutPage.load()
apis.user.sites.deleteSite(siteName)
]);
done();
});
it('Personal Files breadcrumb main node - [C260964]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.clickPersonalFiles();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('Personal Files');
});
it('File Libraries breadcrumb main node - [C260966]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
it('My Libraries breadcrumb main node - [C260966]', async () => {
await page.goToMyLibraries();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('File Libraries');
expect(await breadcrumb.getCurrentItemName()).toBe('My Libraries');
});
it('Favorite Libraries breadcrumb main node - [C289891]', async () => {
await page.goToFavoriteLibraries();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('Favorite Libraries');
});
it('Recent Files breadcrumb main node - [C260971]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await page.clickRecentFiles();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('Recent Files');
});
it('Shared Files breadcrumb main node - [C260972]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.clickSharedFiles();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('Shared Files');
});
it('Favorites breadcrumb main node - [C260973]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await page.clickFavorites();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('Favorites');
});
it('Trash breadcrumb main node - [C260974]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await breadcrumb.getItemsCount()).toEqual(1, 'Breadcrumb has incorrect number of items');
expect(await breadcrumb.getCurrentItemName()).toBe('Trash');
});
it('Personal Files breadcrumb for a folder hierarchy - [C260965]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent);
await page.dataTable.doubleClickOnRowByName(subFolder1);
await page.dataTable.doubleClickOnRowByName(subFolder2);
@@ -132,19 +135,17 @@ describe('Breadcrumb', () => {
});
it('File Libraries breadcrumb for a folder hierarchy - [C260967]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await page.dataTable.waitForHeader();
await page.clickFileLibrariesAndWait();
await page.dataTable.doubleClickOnRowByName(siteName);
await page.dataTable.doubleClickOnRowByName(parent);
await page.dataTable.doubleClickOnRowByName(subFolder1);
await page.dataTable.doubleClickOnRowByName(subFolder2);
const expectedItems = [ 'File Libraries', siteName, parent, subFolder1, subFolder2 ];
const expectedItems = [ 'My Libraries', siteName, parent, subFolder1, subFolder2 ];
expect(await breadcrumb.getAllItems()).toEqual(expectedItems);
});
it('User can navigate to any location by clicking on a step from the breadcrumb - [C213235]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent);
await page.dataTable.doubleClickOnRowByName(subFolder1);
await page.dataTable.doubleClickOnRowByName(subFolder2);
@@ -154,8 +155,7 @@ describe('Breadcrumb', () => {
});
it('Tooltip appears on hover on a step in breadcrumb - [C213237]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent);
await page.dataTable.doubleClickOnRowByName(subFolder1);
await page.dataTable.doubleClickOnRowByName(subFolder2);
@@ -163,8 +163,7 @@ describe('Breadcrumb', () => {
});
it('Breadcrumb updates correctly when folder is renamed - [C213238]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent2);
await page.dataTable.doubleClickOnRowByName(folder1);
await page.dataTable.wait();
@@ -175,12 +174,11 @@ describe('Breadcrumb', () => {
});
it('Browser back navigates to previous location regardless of breadcrumb steps - [C213240]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.dataTable.waitForHeader();
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent);
await page.dataTable.doubleClickOnRowByName(subFolder1);
await page.dataTable.doubleClickOnRowByName(subFolder2);
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
await page.dataTable.waitForEmptyState();
await browser.navigate().back();
const expectedBreadcrumb = [ 'Personal Files', parent, subFolder1, subFolder2 ];
@@ -194,7 +192,6 @@ describe('Breadcrumb', () => {
const user2Api = new RepoClient(user2, user2);
beforeAll(async (done) => {
await logoutPage.load();
await apis.admin.people.createUser({ username: user2 });
userFolderId = (await user2Api.nodes.createFolder(userFolder)).entry.id;
await loginPage.loginWithAdmin();
@@ -202,10 +199,7 @@ describe('Breadcrumb', () => {
});
afterAll(async (done) => {
await Promise.all([
user2Api.nodes.deleteNodeById(userFolderId),
logoutPage.load()
]);
await user2Api.nodes.deleteNodeById(userFolderId);
done();
});
+63 -35
View File
@@ -26,11 +26,10 @@
import { browser } from 'protractor';
import { APP_ROUTES, SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { LoginPage, BrowsingPage } from '../../pages/pages';
describe('Sidebar', () => {
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { sidenav } = page;
@@ -39,85 +38,114 @@ describe('Sidebar', () => {
done();
});
afterAll(async (done) => {
await logoutPage.load();
done();
});
it('has "Personal Files" as default - [C217149]', async () => {
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES);
expect(await sidenav.isActiveByLabel('Personal Files')).toBe(true, 'Active link');
expect(await sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true, 'Active link');
});
it('navigates to "File Libraries" - [C217150]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FILE_LIBRARIES);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true);
it('File Libraries has correct sub-categories - [C217150]', async () => {
await page.clickFileLibraries();
expect(await sidenav.isFileLibrariesMenuExpanded()).toBe(true, 'File Libraries not expanded');
expect(await sidenav.getLink(SIDEBAR_LABELS.MY_LIBRARIES).isPresent()).toBe(true, 'My Libraries link not present');
expect(await sidenav.getLink(SIDEBAR_LABELS.FAVORITE_LIBRARIES).isPresent()).toBe(true, 'Favorite Libraries link not present');
});
it('My Libraries is automatically selected on expanding File Libraries - [C289900]', async () => {
await page.clickFileLibraries();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link not active');
expect(await sidenav.childIsActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active');
});
it('navigate to Favorite Libraries - [C289902]', async () => {
await page.goToFavoriteLibraries();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITE_LIBRARIES);
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link not active');
expect(await sidenav.childIsActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toBe(true, 'Favorite Libraries link not active');
});
it('navigate to My Libraries - [C289901]', async () => {
await page.goToMyLibraries();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link not active');
expect(await sidenav.childIsActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active');
});
it('navigates to "Personal Files" - [C280409]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
await page.clickPersonalFiles();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true);
expect(await sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true, 'Personal Files link not active');
});
it('navigates to "Shared Files" - [C213110]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
await page.clickSharedFiles();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.SHARED_FILES);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.SHARED_FILES)).toBe(true);
expect(await sidenav.isActive(SIDEBAR_LABELS.SHARED_FILES)).toBe(true, 'Shared Files link not active');
});
it('navigates to "Recent Files" - [C213166]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
await page.clickRecentFiles();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.RECENT_FILES);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.RECENT_FILES)).toBe(true);
expect(await sidenav.isActive(SIDEBAR_LABELS.RECENT_FILES)).toBe(true, 'Recent Files link not active');
});
it('navigates to "Favorites" - [C213225]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
await page.clickFavorites();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITES);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.FAVORITES)).toBe(true);
expect(await sidenav.isActive(SIDEBAR_LABELS.FAVORITES)).toBe(true, 'Favorites link not active');
});
it('navigates to "Trash" - [C213216]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
await page.clickTrash();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.TRASHCAN);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.TRASH)).toBe(true);
expect(await sidenav.isActive(SIDEBAR_LABELS.TRASH)).toBe(true, 'Trash link not active');
});
// TODO: incomplete test
xit('Personal Files tooltip - [C217151]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
it('Personal Files tooltip - [C217151]', async () => {
await page.clickPersonalFiles();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.PERSONAL_FILES)).toContain('View your Personal Files');
});
// TODO: incomplete test
xit('File Libraries tooltip - [C217152]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FILE_LIBRARIES)).toContain('Access File Libraries');
it('File Libraries tooltip - [C217152]', async () => {
await page.clickFileLibraries();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FILE_LIBRARIES)).toContain('File Libraries');
});
// TODO: incomplete test
xit('Shared Files tooltip - [C213111]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
it('My Libraries tooltip - [C289916]', async () => {
await page.goToMyLibraries();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.MY_LIBRARIES)).toContain('Access my libraries');
});
// TODO: incomplete test
it('Favorite Libraries tooltip - [C289917]', async () => {
await page.goToFavoriteLibraries();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toContain('Access my favorite libraries');
});
// TODO: incomplete test
it('Shared Files tooltip - [C213111]', async () => {
await page.clickSharedFiles();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.SHARED_FILES)).toContain('View files that have been shared');
});
// TODO: incomplete test
xit('Recent Files tooltip - [C213167]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
it('Recent Files tooltip - [C213167]', async () => {
await page.clickRecentFiles();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.RECENT_FILES)).toContain('View files you recently edited');
});
// TODO: incomplete test
xit('Favorites tooltip - [C217153]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
it('Favorites tooltip - [C217153]', async () => {
await page.clickFavorites();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FAVORITES)).toContain('View your favorite files and folders');
});
// TODO: incomplete test
xit('Trash tooltip - [C217154]', async () => {
await sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
it('Trash tooltip - [C217154]', async () => {
await page.clickTrash();
expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.TRASH)).toContain('View deleted files in the trash');
});
});
@@ -1,112 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { SIDEBAR_LABELS } from '../../configs';
import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages';
import { Utils } from '../../utilities/utils';
import { RepoClient } from '../../utilities/repo-client/repo-client';
describe('Pagination on empty page', () => {
const username = `user-${Utils.random()}`;
const adminApi = new RepoClient();
const loginPage = new LoginPage();
const logoutPage = new LogoutPage();
const page = new BrowsingPage();
const { pagination } = page;
beforeAll(async (done) => {
await adminApi.people.createUser({ username });
await loginPage.loginWith(username);
done();
});
afterAll(async (done) => {
await logoutPage.load();
done();
});
it('Favorites - pagination controls not displayed - [C280111]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES);
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('File Libraries - pagination controls not displayed - [C280084]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Personal Files - pagination controls not displayed - [C280075]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES);
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Recent Files - pagination controls not displayed - [C280102]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES);
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Shared Files - pagination controls not displayed - [C280094]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES);
expect(await pagination.range.isPresent()).toBe(false);
expect(await pagination.maxItems.isPresent()).toBe(false);
expect(await pagination.currentPage.isPresent()).toBe(false);
expect(await pagination.totalPages.isPresent()).toBe(false);
expect(await pagination.previousButton.isPresent()).toBe(false);
expect(await pagination.nextButton.isPresent()).toBe(false);
});
it('Trash - pagination controls not displayed - [C280120]', async () => {
await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH);
expect(await pagination.range.isPresent()).toBe(false);
expect(pagination.maxItems.isPresent()).toBe(false);
expect(pagination.currentPage.isPresent()).toBe(false);
expect(pagination.totalPages.isPresent()).toBe(false);
expect(pagination.previousButton.isPresent()).toBe(false);
expect(pagination.nextButton.isPresent()).toBe(false);
});
});

Some files were not shown because too many files have changed in this diff Show More