diff --git a/.travis.yml b/.travis.yml index e7ba4e4e5..5ee60be5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,4 +37,4 @@ jobs: script: npm run build && npm run e2e:docker - stage: e2e name: 'Tomcat' - script: npm run build.tomcat && npm run docker.tomcat.e2e + script: npm run build.tomcat.e2e && npm run docker.tomcat.e2e diff --git a/Dockerfile b/Dockerfile index acf5650f1..bf3087e39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,9 @@ FROM nginx:stable-alpine -LABEL version="1.3" +LABEL version="1.4" LABEL maintainer="Denys Vuika " - COPY nginx.conf /etc/nginx/nginx.conf -#COPY --chown=nginx:nginx ./docker-entrypoint.sh / COPY ./docker-entrypoint.sh / RUN chmod +x /docker-entrypoint.sh diff --git a/build-tomcat-e2e.sh b/build-tomcat-e2e.sh new file mode 100755 index 000000000..849e7a464 --- /dev/null +++ b/build-tomcat-e2e.sh @@ -0,0 +1,13 @@ +npm run build -- --base-href ./ + +node -e " +const fs = require('fs'); +const config = require('./dist/app/app.config.json'); +config.baseShareUrl = 'http://localhost:4000/content-app'; +fs.writeFileSync( + './dist/app/app.config.json', + JSON.stringify(config, null, 2) +); +" + +jar -cvf docker/tomcat/artifacts/content-app.war -C dist/app/ . diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index b2022d33d..1ce3ab2a3 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -51,8 +51,14 @@ fi if [[ $ACSURL ]]; then sed -i s%{protocol}//{hostname}{:port}%"$ACSURL"%g /usr/share/nginx/html/app.config.json fi + if [[ $BASEPATH ]]; then sed -i s%href=\"/\"%href=\""$BASEPATH"\"%g /usr/share/nginx/html/index.html fi +if [ -n "${APP_BASE_SHARE_URL}" ];then + sed -e "s/\"baseShareUrl\": \".*\"/\"baseShareUrl\": \"${APP_BASE_SHARE_URL}\"/g" \ + -i ./app.config.json +fi + nginx -g "daemon off;" diff --git a/docker/tomcat/docker-compose.yml b/docker/tomcat/docker-compose.yml index 1e0130b75..d5b57119c 100644 --- a/docker/tomcat/docker-compose.yml +++ b/docker/tomcat/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: alfresco: - image: alfresco/alfresco-content-repository-community:6.0.7-ga + image: alfresco/alfresco-content-repository-community:6.1.0-ea depends_on: - postgres environment: @@ -26,7 +26,7 @@ services: - 8080:8080 #Browser port share: - image: alfresco/alfresco-share:6.0.b + image: alfresco/alfresco-share:6.0.c depends_on: - alfresco environment: @@ -50,7 +50,7 @@ services: - 5432:5432 solr6: - image: alfresco/alfresco-search-services:1.1.1 + image: alfresco/alfresco-search-services:1.2.0 depends_on: - alfresco environment: @@ -70,6 +70,8 @@ services: content-app: image: alfresco/alfresco-content-app:development-latest-tomcat8 build: . + environment: + - APP_BASE_SHARE_URL={protocol}//{hostname}{:port}/content-app/ depends_on: - alfresco networks: diff --git a/e2e/components/datetime-picker/datetime-picker.ts b/e2e/components/datetime-picker/datetime-picker.ts new file mode 100755 index 000000000..6cf4e7367 --- /dev/null +++ b/e2e/components/datetime-picker/datetime-picker.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { ElementFinder, by, browser, ExpectedConditions as EC } from 'protractor'; +import { BROWSER_WAIT_TIMEOUT } from '../../configs'; +import { Component } from '../component'; + +export class DateTimePicker extends Component { + private static selectors = { + root: '.mat-datetimepicker-popup', + + header: '.mat-datetimepicker-calendar-header', + year: '.mat-datetimepicker-calendar-header-year', + date: '.mat-datetimepicker-calendar-header-date', + + content: '.mat-datetimepicker-calendar-content', + dayPicker: 'mat-datetimepicker-month-view', + + today: '.mat-datetimepicker-calendar-body-today', + firstActiveDay: '.mat-datetimepicker-calendar-body-active .mat-datetimepicker-calendar-body-cell-content', + }; + + calendar: ElementFinder = browser.element(by.css(DateTimePicker.selectors.root)); + headerDate: ElementFinder = this.component.element(by.css(DateTimePicker.selectors.date)); + headerYear: ElementFinder = this.component.element(by.css(DateTimePicker.selectors.year)); + dayPicker: ElementFinder = this.component.element(by.css(DateTimePicker.selectors.dayPicker)); + + constructor(ancestor?: ElementFinder) { + super(DateTimePicker.selectors.root, ancestor); + } + + async waitForDateTimePickerToOpen() { + await browser.wait(EC.presenceOf(this.calendar), BROWSER_WAIT_TIMEOUT); + } + + async waitForDateTimePickerToClose() { + await browser.wait(EC.stalenessOf(this.calendar), BROWSER_WAIT_TIMEOUT); + } + + async isCalendarOpen() { + return await browser.isElementPresent(by.css(DateTimePicker.selectors.root)); + } + + async getDate() { + return await this.headerDate.getText(); + } + + async getYear() { + return await this.headerYear.getText(); + } + + async setDefaultDay() { + const today = await this.dayPicker.element(by.css(DateTimePicker.selectors.today)).getText(); + const tomorrow = (parseInt(today, 10) + 1).toString(); + const date = await this.getDate(); + const year = await this.getYear(); + const elem = this.dayPicker.element(by.cssContainingText(DateTimePicker.selectors.firstActiveDay, tomorrow)); + await elem.click(); + return `${date} ${year}`; + } +} diff --git a/e2e/components/dialog/share-dialog.ts b/e2e/components/dialog/share-dialog.ts index da0378c88..5cf145320 100755 --- a/e2e/components/dialog/share-dialog.ts +++ b/e2e/components/dialog/share-dialog.ts @@ -23,23 +23,41 @@ * along with Alfresco. If not, see . */ -import { ElementFinder, by, browser, ExpectedConditions as EC } from 'protractor'; +import { ElementFinder, ElementArrayFinder, by, browser, ExpectedConditions as EC } from 'protractor'; import { BROWSER_WAIT_TIMEOUT } from '../../configs'; +import { DateTimePicker } from '../../components/datetime-picker/datetime-picker'; import { Component } from '../component'; export class ShareDialog extends Component { private static selectors = { root: '.adf-share-dialog', - title: '.mat-dialog-title', - content: '.mat-dialog-content', + title: `[data-automation-id='adf-share-dialog-title']`, + info: '.adf-share-link__info', + label: '.adf-share-link__label', + shareToggle: `[data-automation-id='adf-share-toggle']`, + linkUrl: `[data-automation-id='adf-share-link']`, + inputAction: '.input-action', + expireToggle: `[data-automation-id='adf-expire-toggle']`, + datetimePickerButton: '.mat-datetimepicker-toggle', + expirationInput: 'input[formcontrolname="time"]', button: `[data-automation-id='adf-share-dialog-close']` }; + dateTimePicker = new DateTimePicker(); + title: ElementFinder = this.component.element(by.css(ShareDialog.selectors.title)); - content: ElementFinder = this.component.element(by.css(ShareDialog.selectors.content)); + infoText: ElementFinder = this.component.element(by.css(ShareDialog.selectors.info)); + labels: ElementArrayFinder = this.component.all(by.css(ShareDialog.selectors.label)); + shareToggle: ElementFinder = this.component.element(by.css(ShareDialog.selectors.shareToggle)); + url: ElementFinder = this.component.element(by.css(ShareDialog.selectors.linkUrl)); + urlAction: ElementFinder = this.component.element(by.css(ShareDialog.selectors.inputAction)); + expireToggle: ElementFinder = this.component.element(by.css(ShareDialog.selectors.expireToggle)); + expireInput: ElementFinder = this.component.element(by.css(ShareDialog.selectors.expirationInput)); + datetimePickerButton: ElementFinder = this.component.element(by.css(ShareDialog.selectors.datetimePickerButton)); closeButton: ElementFinder = this.component.element(by.css(ShareDialog.selectors.button)); + constructor(ancestor?: ElementFinder) { super(ShareDialog.selectors.root, ancestor); } @@ -48,20 +66,81 @@ export class ShareDialog extends Component { await browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT); } + async waitForDialogToOpen() { + await browser.wait(EC.presenceOf(this.title), BROWSER_WAIT_TIMEOUT); + } + async isDialogOpen() { - return await browser.$(ShareDialog.selectors.root).isDisplayed(); + return await browser.isElementPresent(by.css(ShareDialog.selectors.root)); } async getTitle() { return await this.title.getText(); } - async getText() { - return await this.content.getText(); + async getInfoText() { + return await this.infoText.getText(); + } + + getLabels() { + return this.labels; + } + + async getLinkUrl() { + return await this.url.getAttribute('value'); + } + + async isUrlReadOnly() { + return await this.url.getAttribute('readonly'); } async clickClose() { await this.closeButton.click(); await this.waitForDialogToClose(); } + + getShareToggle() { + return this.shareToggle; + } + + getExpireToggle() { + return this.expireToggle; + } + + getExpireInput() { + return this.expireInput; + } + + async isShareToggleEnabled() { + const toggleClass = await this.getShareToggle().getAttribute('class'); + return toggleClass.includes('checked'); + } + + async isExpireToggleEnabled() { + const toggleClass = await this.getExpireToggle().getAttribute('class'); + return toggleClass.includes('checked'); + } + + async copyUrl() { + return await this.urlAction.click(); + } + + async openDatetimePicker() { + return await this.datetimePickerButton.click(); + } + + async closeDatetimePicker() { + if (await this.dateTimePicker.isCalendarOpen()) { + return await this.datetimePickerButton.click(); + } + } + + async getExpireDate() { + return await this.getExpireInput().getAttribute('value'); + } + + async clickExpirationToggle() { + await this.expireToggle.click(); + } + } diff --git a/e2e/suites/actions/share-file.test.ts b/e2e/suites/actions/share-file.test.ts new file mode 100755 index 000000000..d994a212f --- /dev/null +++ b/e2e/suites/actions/share-file.test.ts @@ -0,0 +1,1085 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { browser } from 'protractor'; +import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; +import { SIDEBAR_LABELS, 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'; +import { Utils } from '../../utilities/utils'; + +describe('Share a file', () => { + const username = `user-${Utils.random()}`; + + const parent = `parent-${Utils.random()}`; let parentId; + + const expiryDate: any = '2020-12-25T18:30:00.000+0000'; + + 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; + const shareDialog = new ShareDialog(); + const viewer = new Viewer(); + const contextMenu = dataTable.menu; + + beforeAll(async (done) => { + await apis.admin.people.createUser({ username }); + + parentId = (await apis.user.nodes.createFolder(parent)).entry.id; + + await loginPage.loginWith(username); + done(); + }); + + afterAll(async (done) => { + await Promise.all([ + apis.user.nodes.deleteNodeById(parentId), + logoutPage.load() + ]); + done(); + }); + + describe('from Personal Files', () => { + + const file1 = `file1PF-${Utils.random()}.txt`; let file1Id; + const file2 = `file2PF-${Utils.random()}.txt`; let file2Id; + const file3 = `file3PF-${Utils.random()}.txt`; let file3Id; + const file4 = `file4PF-${Utils.random()}.txt`; let file4Id; + const file5 = `file5PF-${Utils.random()}.txt`; let file5Id; + const file6 = `file6PF-${Utils.random()}.txt`; let file6Id; + const file7 = `file7PF-${Utils.random()}.txt`; let file7Id; + const file8 = `file8PF-${Utils.random()}.txt`; let file8Id; + const file9 = `file9PF-${Utils.random()}.txt`; let file9Id; + + beforeAll(async (done) => { + file1Id = (await apis.user.nodes.createFile(file1, parentId)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2, parentId)).entry.id; + file3Id = (await apis.user.nodes.createFile(file3, parentId)).entry.id; + file4Id = (await apis.user.nodes.createFile(file4, parentId)).entry.id; + file5Id = (await apis.user.nodes.createFile(file5, parentId)).entry.id; + file6Id = (await apis.user.nodes.createFile(file6, parentId)).entry.id; + file7Id = (await apis.user.nodes.createFile(file7, parentId)).entry.id; + file8Id = (await apis.user.nodes.createFile(file8, parentId)).entry.id; + file9Id = (await apis.user.nodes.createFile(file9, parentId)).entry.id; + await apis.user.shared.shareFileById(file6Id, expiryDate); + await apis.user.shared.shareFileById(file7Id, expiryDate); + await apis.user.shared.waitForApi({ expect: 2 }); + done(); + }); + + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(parent); + await dataTable.waitForHeader(); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.user.nodes.deleteNodeById(file1Id); + await apis.user.nodes.deleteNodeById(file2Id); + await apis.user.nodes.deleteNodeById(file3Id); + await apis.user.nodes.deleteNodeById(file4Id); + await apis.user.nodes.deleteNodeById(file5Id); + await apis.user.nodes.deleteNodeById(file6Id); + await apis.user.nodes.deleteNodeById(file7Id); + await apis.user.nodes.deleteNodeById(file8Id); + await apis.user.nodes.deleteNodeById(file9Id); + await apis.user.shared.waitForApi({ expect: 0 }); + done(); + }); + + it('Share dialog default values - [C286327]', async () => { + await dataTable.selectItem(file1); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); + expect(await shareDialog.getInfoText()).toEqual('Click the link below to copy it to the clipboard.'); + expect(await shareDialog.getLabels().get(0).getText()).toEqual('Link to share'); + expect(await shareDialog.getLinkUrl()).toContain('/preview/s/'); + expect(await shareDialog.isUrlReadOnly()).toBe('true', 'url is not readonly'); + expect(await shareDialog.isShareToggleEnabled()).toBe(true, 'Share toggle not checked'); + expect(await shareDialog.getLabels().get(1).getText()).toEqual('Expires on'); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + }); + + it('Close dialog - [C286328]', async () => { + await dataTable.selectItem(file2); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + await shareDialog.clickClose(); + expect(await shareDialog.isDialogOpen()).toBe(false, 'Share dialog is open'); + }); + + it('Share a file - [C286329]', async () => { + await dataTable.selectItem(file3); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file3Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file3} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // 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 shareDialog.waitForDialogToOpen(); + const url = await shareDialog.getLinkUrl(); + expect(url).toContain('/preview/s/'); + + await shareDialog.copyUrl(); + expect(await page.getSnackBarMessage()).toBe('Link copied to the clipboard'); + + await browser.get(url); + expect(await viewer.isViewerOpened()).toBe(true, 'viewer is not open'); + expect(await viewer.getFileTitle()).toEqual(file4); + + await page.load(); + }); + + it('Share a file with expiration date - [C286332]', async () => { + await dataTable.selectItem(file5); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expire toggle not checked'); + expect(await shareDialog.dateTimePicker.isCalendarOpen()).toBe(true, 'Calendar not opened'); + const date = await shareDialog.dateTimePicker.setDefaultDay(); + await shareDialog.dateTimePicker.waitForDateTimePickerToClose(); + + const setDate = (`${date}`).replace(',', ''); + const inputDate = await shareDialog.getExpireDate(); + + expect(new Date(inputDate)).toEqual(new Date(setDate)); + + const expireDateProperty = await apis.user.nodes.getNodeProperty(file5Id, 'qshare:expiryDate'); + + expect(Utils.formatDate(expireDateProperty)).toEqual(Utils.formatDate(inputDate)); + }); + + it('Expire date is displayed correctly - [C286337]', async () => { + await dataTable.selectItem(file6); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); + expect(expireProperty).toEqual(expiryDate); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(Utils.formatDate(await shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); + }); + + it('Disable the share link expiration - [C286333]', async () => { + await dataTable.selectItem(file7); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(await shareDialog.getExpireDate()).not.toBe('', 'Expire date input is empty'); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expiration is checked'); + expect(await shareDialog.getExpireDate()).toBe('', 'Expire date input is not empty'); + + await shareDialog.clickClose(); + expect(await apis.user.nodes.getNodeProperty(file7Id, 'qshare:expiryDate')).toBe(undefined, `${file7} link still has expiration`); + }); + + 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 shareDialog.waitForDialogToOpen(); + const url1 = await shareDialog.getLinkUrl(); + await shareDialog.clickClose(); + await shareDialog.waitForDialogToClose(); + + await page.dataTable.clearSelection(); + await dataTable.selectItem(file8); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + const url2 = await shareDialog.getLinkUrl(); + + expect(url1).toEqual(url2); + }); + + it('Share a file from the context menu - [C286345]', async () => { + await dataTable.rightClickOnItem(file9); + await contextMenu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file9Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file9} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`); + }); + }); + + describe('from File Libraries', () => { + + const file1 = `file1PF-${Utils.random()}.txt`; let file1Id; + const file2 = `file2PF-${Utils.random()}.txt`; let file2Id; + const file3 = `file2PF-${Utils.random()}.txt`; let file3Id; + const file4 = `file2PF-${Utils.random()}.txt`; let file4Id; + const file5 = `file2PF-${Utils.random()}.txt`; let file5Id; + const file6 = `file2PF-${Utils.random()}.txt`; let file6Id; + const file7 = `file2PF-${Utils.random()}.txt`; let file7Id; + const file8 = `file2PF-${Utils.random()}.txt`; let file8Id; + const file9 = `file2PF-${Utils.random()}.txt`; let file9Id; + + const siteName = `site-${Utils.random()}`; + const parentInSite = `parent-site-${Utils.random()}`; let parentInSiteId; + + beforeAll(async (done) => { + await apis.user.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); + const docLibId = await apis.user.sites.getDocLibId(siteName); + parentInSiteId = (await apis.user.nodes.createFolder(parentInSite, docLibId)).entry.id; + + file1Id = (await apis.user.nodes.createFile(file1, parentInSiteId)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2, parentInSiteId)).entry.id; + file3Id = (await apis.user.nodes.createFile(file3, parentInSiteId)).entry.id; + file4Id = (await apis.user.nodes.createFile(file4, parentInSiteId)).entry.id; + file5Id = (await apis.user.nodes.createFile(file5, parentInSiteId)).entry.id; + file6Id = (await apis.user.nodes.createFile(file6, parentInSiteId)).entry.id; + file7Id = (await apis.user.nodes.createFile(file7, parentInSiteId)).entry.id; + file8Id = (await apis.user.nodes.createFile(file8, parentInSiteId)).entry.id; + file9Id = (await apis.user.nodes.createFile(file9, parentInSiteId)).entry.id; + await apis.user.shared.shareFileById(file6Id, expiryDate); + await apis.user.shared.shareFileById(file7Id, expiryDate); + await apis.user.shared.waitForApi({ expect: 2 }); + done(); + }); + + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(siteName); + await dataTable.waitForHeader(); + await dataTable.doubleClickOnRowByName(parentInSite); + await dataTable.waitForHeader(); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.admin.sites.deleteSite(siteName); + await apis.user.shared.waitForApi({ expect: 0 }); + done(); + }); + + it('Share dialog default values - [C286639]', async () => { + await dataTable.selectItem(file1); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); + expect(await shareDialog.getInfoText()).toEqual('Click the link below to copy it to the clipboard.'); + expect(await shareDialog.getLabels().get(0).getText()).toEqual('Link to share'); + expect(await shareDialog.getLinkUrl()).toContain('/preview/s/'); + expect(await shareDialog.isUrlReadOnly()).toBe('true', 'url is not readonly'); + expect(await shareDialog.isShareToggleEnabled()).toBe(true, 'Share toggle not checked'); + expect(await shareDialog.getLabels().get(1).getText()).toEqual('Expires on'); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + }); + + it('Close dialog - [C286640]', async () => { + await dataTable.selectItem(file2); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + await shareDialog.clickClose(); + expect(await shareDialog.isDialogOpen()).toBe(false, 'Share dialog is open'); + }); + + it('Share a file - [C286641]', async () => { + await dataTable.selectItem(file3); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file3Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file3} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // 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 shareDialog.waitForDialogToOpen(); + const url = await shareDialog.getLinkUrl(); + expect(url).toContain('/preview/s/'); + + await shareDialog.copyUrl(); + expect(await page.getSnackBarMessage()).toBe('Link copied to the clipboard'); + + await browser.get(url); + expect(await viewer.isViewerOpened()).toBe(true, 'viewer is not open'); + expect(await viewer.getFileTitle()).toEqual(file4); + + await page.load(); + }); + + it('Share a file with expiration date - [C286643]', async () => { + await dataTable.selectItem(file5); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expire toggle not checked'); + expect(await shareDialog.dateTimePicker.isCalendarOpen()).toBe(true, 'Calendar not opened'); + const date = await shareDialog.dateTimePicker.setDefaultDay(); + await shareDialog.dateTimePicker.waitForDateTimePickerToClose(); + + const setDate = (`${date}`).replace(',', ''); + const inputDate = await shareDialog.getExpireDate(); + + expect(new Date(inputDate)).toEqual(new Date(setDate)); + + const expireDateProperty = await apis.user.nodes.getNodeProperty(file5Id, 'qshare:expiryDate'); + + expect(Utils.formatDate(expireDateProperty)).toEqual(Utils.formatDate(inputDate)); + }); + + it('Expire date is displayed correctly - [C286644]', async () => { + await dataTable.selectItem(file6); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); + expect(expireProperty).toEqual(expiryDate); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(Utils.formatDate(await shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); + }); + + it('Disable the share link expiration - [C286645]', async () => { + await dataTable.selectItem(file7); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(await shareDialog.getExpireDate()).not.toBe('', 'Expire date input is empty'); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expiration is checked'); + expect(await shareDialog.getExpireDate()).toBe('', 'Expire date input is not empty'); + + await shareDialog.clickClose(); + expect(await apis.user.nodes.getNodeProperty(file7Id, 'qshare:expiryDate')).toBe(undefined, `${file7} link still has expiration`); + }); + + 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 shareDialog.waitForDialogToOpen(); + const url1 = await shareDialog.getLinkUrl(); + await shareDialog.clickClose(); + await shareDialog.waitForDialogToClose(); + + await page.dataTable.clearSelection(); + await dataTable.selectItem(file8); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + const url2 = await shareDialog.getLinkUrl(); + + expect(url1).toEqual(url2); + }); + + it('Share a file from the context menu - [C286647]', async () => { + await dataTable.rightClickOnItem(file9); + await contextMenu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file9Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file9} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); + }); + }); + + describe('from Recent Files', () => { + + const file1 = `file1PF-${Utils.random()}.txt`; let file1Id; + const file2 = `file2PF-${Utils.random()}.txt`; let file2Id; + const file3 = `file2PF-${Utils.random()}.txt`; let file3Id; + const file4 = `file2PF-${Utils.random()}.txt`; let file4Id; + const file5 = `file2PF-${Utils.random()}.txt`; let file5Id; + const file6 = `file2PF-${Utils.random()}.txt`; let file6Id; + const file7 = `file2PF-${Utils.random()}.txt`; let file7Id; + const file8 = `file2PF-${Utils.random()}.txt`; let file8Id; + const file9 = `file2PF-${Utils.random()}.txt`; let file9Id; + + beforeAll(async (done) => { + file1Id = (await apis.user.nodes.createFile(file1, parentId)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2, parentId)).entry.id; + file3Id = (await apis.user.nodes.createFile(file3, parentId)).entry.id; + file4Id = (await apis.user.nodes.createFile(file4, parentId)).entry.id; + file5Id = (await apis.user.nodes.createFile(file5, parentId)).entry.id; + file6Id = (await apis.user.nodes.createFile(file6, parentId)).entry.id; + file7Id = (await apis.user.nodes.createFile(file7, parentId)).entry.id; + file8Id = (await apis.user.nodes.createFile(file8, parentId)).entry.id; + file9Id = (await apis.user.nodes.createFile(file9, parentId)).entry.id; + await apis.user.shared.shareFileById(file6Id, expiryDate); + await apis.user.shared.shareFileById(file7Id, expiryDate); + await apis.user.shared.waitForApi({ expect: 2 }); + done(); + }); + + beforeEach(async (done) => { + await page.refresh(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES); + await dataTable.waitForHeader(); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.user.nodes.deleteNodeById(file1Id); + await apis.user.nodes.deleteNodeById(file2Id); + await apis.user.nodes.deleteNodeById(file3Id); + await apis.user.nodes.deleteNodeById(file4Id); + await apis.user.nodes.deleteNodeById(file5Id); + await apis.user.nodes.deleteNodeById(file6Id); + await apis.user.nodes.deleteNodeById(file7Id); + await apis.user.nodes.deleteNodeById(file8Id); + await apis.user.nodes.deleteNodeById(file9Id); + await apis.user.shared.waitForApi({ expect: 0 }); + done(); + }); + + it('Share dialog default values - [C286657]', async () => { + await dataTable.selectItem(file1); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); + expect(await shareDialog.getInfoText()).toEqual('Click the link below to copy it to the clipboard.'); + expect(await shareDialog.getLabels().get(0).getText()).toEqual('Link to share'); + expect(await shareDialog.getLinkUrl()).toContain('/preview/s/'); + expect(await shareDialog.isUrlReadOnly()).toBe('true', 'url is not readonly'); + expect(await shareDialog.isShareToggleEnabled()).toBe(true, 'Share toggle not checked'); + expect(await shareDialog.getLabels().get(1).getText()).toEqual('Expires on'); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + }); + + it('Close dialog - [C286658]', async () => { + await dataTable.selectItem(file2); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + await shareDialog.clickClose(); + expect(await shareDialog.isDialogOpen()).toBe(false, 'Share dialog is open'); + }); + + it('Share a file - [C286659]', async () => { + await dataTable.selectItem(file3); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file3Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file3} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // 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 shareDialog.waitForDialogToOpen(); + const url = await shareDialog.getLinkUrl(); + expect(url).toContain('/preview/s/'); + + await shareDialog.copyUrl(); + expect(await page.getSnackBarMessage()).toBe('Link copied to the clipboard'); + + await browser.get(url); + expect(await viewer.isViewerOpened()).toBe(true, 'viewer is not open'); + expect(await viewer.getFileTitle()).toEqual(file4); + + await page.load(); + }); + + it('Share a file with expiration date - [C286661]', async () => { + await dataTable.selectItem(file5); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expire toggle not checked'); + expect(await shareDialog.dateTimePicker.isCalendarOpen()).toBe(true, 'Calendar not opened'); + const date = await shareDialog.dateTimePicker.setDefaultDay(); + await shareDialog.dateTimePicker.waitForDateTimePickerToClose(); + + const setDate = (`${date}`).replace(',', ''); + const inputDate = await shareDialog.getExpireDate(); + + expect(new Date(inputDate)).toEqual(new Date(setDate)); + + const expireDateProperty = await apis.user.nodes.getNodeProperty(file5Id, 'qshare:expiryDate'); + + expect(Utils.formatDate(expireDateProperty)).toEqual(Utils.formatDate(inputDate)); + }); + + it('Expire date is displayed correctly - [C286662]', async () => { + await dataTable.selectItem(file6); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); + expect(expireProperty).toEqual(expiryDate); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(Utils.formatDate(await shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); + }); + + it('Disable the share link expiration - [C286663]', async () => { + await dataTable.selectItem(file7); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(await shareDialog.getExpireDate()).not.toBe('', 'Expire date input is empty'); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expiration is checked'); + expect(await shareDialog.getExpireDate()).toBe('', 'Expire date input is not empty'); + + await shareDialog.clickClose(); + expect(await apis.user.nodes.getNodeProperty(file7Id, 'qshare:expiryDate')).toBe(undefined, `${file7} link still has expiration`); + }); + + 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 shareDialog.waitForDialogToOpen(); + const url1 = await shareDialog.getLinkUrl(); + await shareDialog.clickClose(); + await shareDialog.waitForDialogToClose(); + + await page.dataTable.clearSelection(); + await dataTable.selectItem(file8); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + const url2 = await shareDialog.getLinkUrl(); + + expect(url1).toEqual(url2); + }); + + it('Share a file from the context menu - [C286665]', async () => { + await dataTable.rightClickOnItem(file9); + await contextMenu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file9Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file9} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); + }); + }); + + describe('from Shared Files', () => { + + const file1 = `file1PF-${Utils.random()}.txt`; let file1Id; + const file2 = `file2PF-${Utils.random()}.txt`; let file2Id; + const file3 = `file2PF-${Utils.random()}.txt`; let file3Id; + const file4 = `file2PF-${Utils.random()}.txt`; let file4Id; + const file5 = `file2PF-${Utils.random()}.txt`; let file5Id; + const file6 = `file2PF-${Utils.random()}.txt`; let file6Id; + const file7 = `file2PF-${Utils.random()}.txt`; let file7Id; + + beforeAll(async (done) => { + file1Id = (await apis.user.nodes.createFile(file1, parentId)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2, parentId)).entry.id; + file3Id = (await apis.user.nodes.createFile(file3, parentId)).entry.id; + file4Id = (await apis.user.nodes.createFile(file4, parentId)).entry.id; + file5Id = (await apis.user.nodes.createFile(file5, parentId)).entry.id; + file6Id = (await apis.user.nodes.createFile(file6, parentId)).entry.id; + file7Id = (await apis.user.nodes.createFile(file7, parentId)).entry.id; + + await apis.user.shared.shareFileById(file1Id); + await apis.user.shared.shareFileById(file2Id); + await apis.user.shared.shareFileById(file3Id); + await apis.user.shared.shareFileById(file4Id, expiryDate); + await apis.user.shared.shareFileById(file5Id, expiryDate); + await apis.user.shared.shareFileById(file6Id); + await apis.user.shared.shareFileById(file7Id); + await apis.user.shared.waitForApi({ expect: 7 }); + done(); + }); + + beforeEach(async (done) => { + await page.refresh(); + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + await dataTable.waitForHeader(); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.user.nodes.deleteNodeById(file1Id); + await apis.user.nodes.deleteNodeById(file2Id); + await apis.user.nodes.deleteNodeById(file3Id); + await apis.user.nodes.deleteNodeById(file4Id); + await apis.user.nodes.deleteNodeById(file5Id); + await apis.user.nodes.deleteNodeById(file6Id); + await apis.user.nodes.deleteNodeById(file7Id); + await apis.user.shared.waitForApi({ expect: 0 }); + done(); + }); + + it('Share dialog default values - [C286648]', async () => { + await dataTable.selectItem(file1); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); + expect(await shareDialog.getInfoText()).toEqual('Click the link below to copy it to the clipboard.'); + expect(await shareDialog.getLabels().get(0).getText()).toEqual('Link to share'); + expect(await shareDialog.getLinkUrl()).toContain('/preview/s/'); + expect(await shareDialog.isUrlReadOnly()).toBe('true', 'url is not readonly'); + expect(await shareDialog.isShareToggleEnabled()).toBe(true, 'Share toggle not checked'); + expect(await shareDialog.getLabels().get(1).getText()).toEqual('Expires on'); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + }); + + it('Close dialog - [C286649]', async () => { + await dataTable.selectItem(file2); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + await shareDialog.clickClose(); + expect(await shareDialog.isDialogOpen()).toBe(false, 'Share dialog is open'); + }); + + it('Copy shared file URL - [C286651]', async () => { + await dataTable.selectItem(file3); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + const url = await shareDialog.getLinkUrl(); + expect(url).toContain('/preview/s/'); + + await shareDialog.copyUrl(); + expect(await page.getSnackBarMessage()).toBe('Link copied to the clipboard'); + + await browser.get(url); + expect(await viewer.isViewerOpened()).toBe(true, 'viewer is not open'); + expect(await viewer.getFileTitle()).toEqual(file3); + + await page.load(); + }); + + it('Expire date is displayed correctly - [C286653]', async () => { + await dataTable.selectItem(file4); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + const expireProperty = await apis.user.nodes.getNodeProperty(file4Id, 'qshare:expiryDate'); + expect(expireProperty).toEqual(expiryDate); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(Utils.formatDate(await shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); + }); + + it('Disable the share link expiration - [C286654]', async () => { + await dataTable.selectItem(file5); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(await shareDialog.getExpireDate()).not.toBe('', 'Expire date input is empty'); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expiration is checked'); + expect(await shareDialog.getExpireDate()).toBe('', 'Expire date input is not empty'); + + await shareDialog.clickClose(); + expect(await apis.user.nodes.getNodeProperty(file5Id, 'qshare:expiryDate')).toBe(undefined, `${file5} link still has expiration`); + }); + + 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 shareDialog.waitForDialogToOpen(); + const url1 = await shareDialog.getLinkUrl(); + await shareDialog.clickClose(); + await shareDialog.waitForDialogToClose(); + + await page.dataTable.clearSelection(); + await dataTable.selectItem(file6); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + const url2 = await shareDialog.getLinkUrl(); + + expect(url1).toEqual(url2); + }); + + it('Open Share dialog from context menu - [C286656]', async () => { + await dataTable.rightClickOnItem(file7); + await contextMenu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.getTitle()).toEqual(`Share ${file7}`); + expect(await shareDialog.getInfoText()).toEqual('Click the link below to copy it to the clipboard.'); + expect(await shareDialog.getLabels().get(0).getText()).toEqual('Link to share'); + expect(await shareDialog.getLinkUrl()).toContain('/preview/s/'); + expect(await shareDialog.isUrlReadOnly()).toBe('true', 'url is not readonly'); + expect(await shareDialog.isShareToggleEnabled()).toBe(true, 'Share toggle not checked'); + expect(await shareDialog.getLabels().get(1).getText()).toEqual('Expires on'); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + }); + }); + + // TODO: enable when ACA-1886 is done + xdescribe('from Favorites', () => { + + const file1 = `file1PF-${Utils.random()}.txt`; let file1Id; + const file2 = `file2PF-${Utils.random()}.txt`; let file2Id; + const file3 = `file2PF-${Utils.random()}.txt`; let file3Id; + const file4 = `file2PF-${Utils.random()}.txt`; let file4Id; + const file5 = `file2PF-${Utils.random()}.txt`; let file5Id; + const file6 = `file2PF-${Utils.random()}.txt`; let file6Id; + const file7 = `file2PF-${Utils.random()}.txt`; let file7Id; + const file8 = `file2PF-${Utils.random()}.txt`; let file8Id; + const file9 = `file2PF-${Utils.random()}.txt`; let file9Id; + + beforeAll(async (done) => { + file1Id = (await apis.user.nodes.createFile(file1, parentId)).entry.id; + file2Id = (await apis.user.nodes.createFile(file2, parentId)).entry.id; + file3Id = (await apis.user.nodes.createFile(file3, parentId)).entry.id; + file4Id = (await apis.user.nodes.createFile(file4, parentId)).entry.id; + file5Id = (await apis.user.nodes.createFile(file5, parentId)).entry.id; + file6Id = (await apis.user.nodes.createFile(file6, parentId)).entry.id; + file7Id = (await apis.user.nodes.createFile(file7, parentId)).entry.id; + file8Id = (await apis.user.nodes.createFile(file8, parentId)).entry.id; + file9Id = (await apis.user.nodes.createFile(file9, parentId)).entry.id; + + await apis.user.favorites.addFavoriteById('file', file1Id); + await apis.user.favorites.addFavoriteById('file', file2Id); + await apis.user.favorites.addFavoriteById('file', file3Id); + await apis.user.favorites.addFavoriteById('file', file4Id); + await apis.user.favorites.addFavoriteById('file', file5Id); + await apis.user.favorites.addFavoriteById('file', file6Id); + await apis.user.favorites.addFavoriteById('file', file7Id); + await apis.user.favorites.addFavoriteById('file', file8Id); + await apis.user.favorites.addFavoriteById('file', file9Id); + + await apis.user.shared.shareFileById(file6Id, expiryDate); + await apis.user.shared.shareFileById(file7Id, expiryDate); + await apis.user.favorites.waitForApi({ expect: 9 }); + await apis.user.shared.waitForApi({ expect: 2 }); + done(); + }); + + beforeEach(async (done) => { + await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES); + await dataTable.waitForHeader(); + done(); + }); + + afterEach(async (done) => { + await Utils.pressEscape(); + done(); + }); + + afterAll(async (done) => { + await apis.user.nodes.deleteNodeById(file1Id); + await apis.user.nodes.deleteNodeById(file2Id); + await apis.user.nodes.deleteNodeById(file3Id); + await apis.user.nodes.deleteNodeById(file4Id); + await apis.user.nodes.deleteNodeById(file5Id); + await apis.user.nodes.deleteNodeById(file6Id); + await apis.user.nodes.deleteNodeById(file7Id); + await apis.user.nodes.deleteNodeById(file8Id); + await apis.user.nodes.deleteNodeById(file9Id); + await apis.user.shared.waitForApi({ expect: 0 }); + done(); + }); + + xit('Share dialog default values - [C286666]', async () => { + await dataTable.selectItem(file1); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.getTitle()).toEqual(`Share ${file1}`); + expect(await shareDialog.getInfoText()).toEqual('Click the link below to copy it to the clipboard.'); + expect(await shareDialog.getLabels().get(0).getText()).toEqual('Link to share'); + expect(await shareDialog.getLinkUrl()).toContain('/preview/s/'); + expect(await shareDialog.isUrlReadOnly()).toBe('true', 'url is not readonly'); + expect(await shareDialog.isShareToggleEnabled()).toBe(true, 'Share toggle not checked'); + expect(await shareDialog.getLabels().get(1).getText()).toEqual('Expires on'); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + }); + + xit('Close dialog - [C286667]', async () => { + await dataTable.selectItem(file2); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.closeButton.isEnabled()).toBe(true, 'Close button is not enabled'); + await shareDialog.clickClose(); + expect(await shareDialog.isDialogOpen()).toBe(false, 'Share dialog is open'); + }); + + xit('Share a file - [C286668]', async () => { + await dataTable.selectItem(file3); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file3Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file3} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // expect(await dataTable.getRowByName(file3).isPresent()).toBe(true, `${file3} is not in the Shared files list`); + }); + + xit('Copy shared file URL - [C286669]', async () => { + await dataTable.selectItem(file4); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + const url = await shareDialog.getLinkUrl(); + expect(url).toContain('/preview/s/'); + + await shareDialog.copyUrl(); + expect(await page.getSnackBarMessage()).toBe('Link copied to the clipboard'); + + await browser.get(url); + expect(await viewer.isViewerOpened()).toBe(true, 'viewer is not open'); + expect(await viewer.getFileTitle()).toEqual(file4); + + await page.load(); + }); + + xit('Share a file with expiration date - [C286670]', async () => { + await dataTable.selectItem(file5); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expire toggle not checked'); + expect(await shareDialog.dateTimePicker.isCalendarOpen()).toBe(true, 'Calendar not opened'); + const date = await shareDialog.dateTimePicker.setDefaultDay(); + await shareDialog.dateTimePicker.waitForDateTimePickerToClose(); + + const setDate = (`${date}`).replace(',', ''); + const inputDate = await shareDialog.getExpireDate(); + + expect(new Date(inputDate)).toEqual(new Date(setDate)); + + const expireDateProperty = await apis.user.nodes.getNodeProperty(file5Id, 'qshare:expiryDate'); + + expect(Utils.formatDate(expireDateProperty)).toEqual(Utils.formatDate(inputDate)); + }); + + xit('Expire date is displayed correctly - [C286671]', async () => { + await dataTable.selectItem(file6); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + const expireProperty = await apis.user.nodes.getNodeProperty(file6Id, 'qshare:expiryDate'); + expect(expireProperty).toEqual(expiryDate); + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(Utils.formatDate(await shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); + }); + + xit('Disable the share link expiration - [C286672]', async () => { + await dataTable.selectItem(file7); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + + expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); + expect(await shareDialog.getExpireDate()).not.toBe('', 'Expire date input is empty'); + + await shareDialog.clickExpirationToggle(); + expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expiration is checked'); + expect(await shareDialog.getExpireDate()).toBe('', 'Expire date input is not empty'); + + await shareDialog.clickClose(); + expect(await apis.user.nodes.getNodeProperty(file7Id, 'qshare:expiryDate')).toBe(undefined, `${file7} link still has expiration`); + }); + + xit('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 shareDialog.waitForDialogToOpen(); + const url1 = await shareDialog.getLinkUrl(); + await shareDialog.clickClose(); + await shareDialog.waitForDialogToClose(); + + await page.dataTable.clearSelection(); + await dataTable.selectItem(file8); + await toolbar.openMoreMenu(); + await toolbar.menu.clickMenuItem('Shared link settings'); + await shareDialog.waitForDialogToOpen(); + const url2 = await shareDialog.getLinkUrl(); + + expect(url1).toEqual(url2); + }); + + xit('Share a file from the context menu - [C286674]', async () => { + await dataTable.rightClickOnItem(file9); + await contextMenu.clickMenuItem('Share'); + await shareDialog.waitForDialogToOpen(); + + const url = await shareDialog.getLinkUrl(); + await Utils.pressEscape(); + const sharedId = await apis.user.nodes.getNodeProperty(file9Id, 'qshare:sharedId'); + + expect(sharedId).not.toBe(undefined, `${file9} is not shared`); + expect(url).toContain(sharedId); + + // TODO: disable check cause api is slow to update + // await apis.user.shared.waitForApi({ expect: 5 }); + // await page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES); + // expect(await dataTable.getRowByName(file9).isPresent()).toBe(true, `${file9} is not in the Shared files list`); + }); + }); + + xit(''); + +}); diff --git a/e2e/suites/extensions/ext-metadata.test.ts b/e2e/suites/extensions/ext-metadata.test.ts index 33e46f715..a61dfe727 100644 --- a/e2e/suites/extensions/ext-metadata.test.ts +++ b/e2e/suites/extensions/ext-metadata.test.ts @@ -100,14 +100,14 @@ describe('Extensions - Metadata presets', () => { done(); }); - it('Set groups of properties to display - []', async () => { + it('Set groups of properties to display - [C286636]', async () => { expect(await metadataCard.isExpansionPanelPresent(0)).toBe(true, `expansion panel is not present`); expect(await metadataCard.getComponentIdOfPanel(0)).toEqual(`adf-metadata-group-${customGroup1.title}`); expect(await metadataCard.isExpansionPanelPresent(1)).toBe(true, `expansion panel is not present`); expect(await metadataCard.getComponentIdOfPanel(1)).toEqual(`adf-metadata-group-${customGroup2.title}`); }); - it('Disabled group is not displayed - []', async () => { + it('Disabled group is not displayed - [C286637]', async () => { expect(await metadataCard.isExpansionPanelPresent(2)).toBe(false, `disabled group is displayed`); expect(await metadataCard.getComponentIdOfPanel(1)).not.toEqual(`adf-metadata-group-${disabledGroup.title}`); expect(await metadataCard.getComponentIdOfPanel(0)).not.toEqual(`adf-metadata-group-${disabledGroup.title}`); diff --git a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts index 000a25a23..234a0472c 100755 --- a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts +++ b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts @@ -54,6 +54,10 @@ export class NodesApi extends RepoApi { return (await this.getNodeByPath(`${relativePath}`)).entry.properties['cm:description']; } + async getNodeProperty(nodeId: string, property: string) { + return (await this.getNodeById(nodeId)).entry.properties[property]; + } + async deleteNodeById(id: string, permanent: boolean = true) { await this.apiAuth(); return await this.alfrescoJsApi.core.nodesApi.deleteNode(id, { permanent }); diff --git a/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts b/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts index 700899d76..09b088223 100755 --- a/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts +++ b/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts @@ -32,10 +32,17 @@ export class SharedLinksApi extends RepoApi { super(username, password); } - async shareFileById(id: string) { + async shareFileById(id: string, expireDate?: Date) { + try { await this.apiAuth(); - const data = { nodeId: id }; - return await this.alfrescoJsApi.core.sharedlinksApi.addSharedLink(data); + const data = { + nodeId: id, + expiresAt: expireDate + }; + return await this.alfrescoJsApi.core.sharedlinksApi.addSharedLink(data); + } catch (error) { + console.log('---- shareFileById error: ', error); + } } async shareFilesByIds(ids: string[]) { diff --git a/e2e/utilities/utils.ts b/e2e/utilities/utils.ts index a1f96b7f0..7adbaa1eb 100755 --- a/e2e/utilities/utils.ts +++ b/e2e/utilities/utils.ts @@ -115,4 +115,8 @@ export class Utils { static getBrowserLog() { return browser.manage().logs().get('browser'); } + + static formatDate(date: string) { + return new Date(date).toLocaleDateString('en-US'); + } } diff --git a/package.json b/package.json index dfb0a2b84..7e521048d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/app/stats.json", "format:check": "prettier --list-different \"src/{app,environments}/**/*{.ts,.js,.css,.scss}\"", "build.tomcat": "npm run build -- --base-href ./ && jar -cvf docker/tomcat/artifacts/content-app.war -C dist/app/ .", + "build.tomcat.e2e": "./build-tomcat-e2e.sh", "e2e.tomcat": "npm run wd:update && protractor --baseUrl=http://localhost:4000/content-app/", "docker.tomcat.start": "cd docker/tomcat && docker-compose up -d --build && wait-on http://localhost:8080 && wait-on http://localhost:4000", "docker.tomcat.stop": "cd docker/tomcat && docker-compose stop", diff --git a/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts b/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts index 8dc2ad96c..7f497c433 100644 --- a/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts +++ b/src/app/components/shared/content-node-share/content-node-share.dialog.spec.ts @@ -317,7 +317,7 @@ describe('ShareDialogComponent', () => { fixture.detectChanges(); expect(nodesApiService.updateNode).toHaveBeenCalledWith('nodeId', { - properties: { 'qshare:expiryDate': date.utc().format() } + properties: { 'qshare:expiryDate': date } }); }); }); diff --git a/src/app/components/shared/content-node-share/content-node-share.dialog.ts b/src/app/components/shared/content-node-share/content-node-share.dialog.ts index 5f69011ce..826d5890f 100644 --- a/src/app/components/shared/content-node-share/content-node-share.dialog.ts +++ b/src/app/components/shared/content-node-share/content-node-share.dialog.ts @@ -103,7 +103,7 @@ export class ShareDialogComponent implements OnInit, OnDestroy { this.baseShareUrl = this.data.baseShareUrl; const properties = this.data.node.entry.properties; - if (properties && !properties['qshare:sharedId']) { + if (!properties || !properties['qshare:sharedId']) { this.createSharedLinks(this.data.node.entry.id); } else { this.sharedId = properties['qshare:sharedId']; @@ -174,7 +174,13 @@ export class ShareDialogComponent implements OnInit, OnDestroy { (sharedLink: SharedLinkEntry) => { if (sharedLink.entry) { this.sharedId = sharedLink.entry.id; - this.data.node.entry.properties['qshare:sharedId'] = this.sharedId; + if (this.data.node.entry.properties) { + this.data.node.entry.properties['qshare:sharedId'] = this.sharedId; + } else { + this.data.node.entry.properties = { + 'qshare:sharedId': this.sharedId + }; + } this.isDisabled = false; this.isFileShared = true; @@ -217,12 +223,7 @@ export class ShareDialogComponent implements OnInit, OnDestroy { private updateNode(date: moment.Moment): Observable { return this.nodesApiService.updateNode(this.data.node.entry.id, { properties: { - 'qshare:expiryDate': date - ? date - .utc() - .endOf('day') - .format() - : null + 'qshare:expiryDate': date ? date.endOf('day') : null } }); } @@ -230,6 +231,6 @@ export class ShareDialogComponent implements OnInit, OnDestroy { private updateEntryExpiryDate(date: moment.Moment) { const { properties } = this.data.node.entry; - properties['qshare:expiryDate'] = date ? date.local() : null; + properties['qshare:expiryDate'] = date ? date.toDate() : null; } }