From 7162b7215e787eaef425d5d5762a42044b4ebc79 Mon Sep 17 00:00:00 2001 From: Pascal Wengerter Date: Wed, 18 May 2022 22:24:07 +0200 Subject: [PATCH] Fix E2E tests --- .../components/SideBar/Shares/FileLinks.vue | 15 +- .../SideBar/Shares/Links/DetailsAndEdit.vue | 1 + .../__snapshots__/DetailsAndEdit.spec.js.snap | 2 +- .../features/integrations/link.feature | 8 +- .../integrations/spaces/project.ocis.feature | 53 ++++--- tests/e2e/cucumber/steps/app-files/link.ts | 62 +++++--- .../cucumber/steps/app-files/page/public.ts | 2 +- tests/e2e/support/environment/link.ts | 17 +- .../support/objects/app-files/link/actions.ts | 147 +++++++++++------- .../support/objects/app-files/link/index.ts | 36 ++++- 10 files changed, 228 insertions(+), 115 deletions(-) diff --git a/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue b/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue index ab93d40299a..6c34fe7fe32 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue @@ -124,10 +124,6 @@ export default defineComponent({ return this.linkListCollapsed ? this.$gettext('Show more') : this.$gettext('Show less') }, - defaultNewLinkName() { - return this.$gettext('Link') - }, - quicklink() { return this.currentFileOutgoingLinks.find((link) => link.quicklink === true) }, @@ -173,7 +169,6 @@ export default defineComponent({ label: this.$gettext(role.label) } }) - // add empty permission link if oCIS for alias link return [ // { role: null, name: 'Alias link', label: this.$gettext('Only invited people') }, @@ -238,10 +233,14 @@ export default defineComponent({ }, displayLinks() { + const linkShares = this.links + const sortedLinkShares = linkShares.sort((a, b) => { + return b.stime - a.stime + }) if (this.links.length > 3 && this.linkListCollapsed) { - return this.links.slice(0, 3) + return sortedLinkShares.slice(0, 3) } - return this.links + return sortedLinkShares }, indirectLinks() { @@ -342,7 +341,7 @@ export default defineComponent({ addNewLink() { this.createLink({ link: { - name: this.newLinkName, + name: this.$gettext('Link'), role: this.availableRoleOptions[0], expiration: this.expirationDate.default, password: null diff --git a/packages/web-app-files/src/components/SideBar/Shares/Links/DetailsAndEdit.vue b/packages/web-app-files/src/components/SideBar/Shares/Links/DetailsAndEdit.vue index 810ef8d14a6..df1df2229d0 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Links/DetailsAndEdit.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Links/DetailsAndEdit.vue @@ -107,6 +107,7 @@ Rename
  • - +
  • Add password diff --git a/tests/e2e/cucumber/features/integrations/link.feature b/tests/e2e/cucumber/features/integrations/link.feature index 552de51f0ad..e6d4511e494 100644 --- a/tests/e2e/cucumber/features/integrations/link.feature +++ b/tests/e2e/cucumber/features/integrations/link.feature @@ -14,9 +14,11 @@ Feature: link | lorem.txt | folderPublic | #Then "Alice" should see the following resource # | folderPublic/lorem.txt | - And "Alice" creates a public link for the following resource using the sidebar panel - | resource | name | role | dateOfExpiration | password | - | folderPublic | myPublicLink | uploader | +5 days | 12345 | + And "Alice" creates a public link for the resource "folderPublic" using the sidebar panel + And "Alice" renames the most recently created public link of resource "folderPublic" to "myPublicLink" + And "Alice" edits the public link named "myPublicLink" of resource "folderPublic" changing role to "uploader" + And "Alice" sets the expiration date of the public link named "myPublicLink" of resource "folderPublic" to "+5 days" + And "Alice" sets the password of the public link named "myPublicLink" of resource "folderPublic" to "12345" #Then "Alice" should see 1 public link When "Anonymous" opens the public link "myPublicLink" And "Anonymous" unlocks the public link with password "12345" diff --git a/tests/e2e/cucumber/features/integrations/spaces/project.ocis.feature b/tests/e2e/cucumber/features/integrations/spaces/project.ocis.feature index 4d3e605a713..fdb2b64d2a6 100644 --- a/tests/e2e/cucumber/features/integrations/spaces/project.ocis.feature +++ b/tests/e2e/cucumber/features/integrations/spaces/project.ocis.feature @@ -1,20 +1,20 @@ Feature: spaces.personal Scenario: unstructured collection of testable space interactions, - once all needed features are there, split this into independent tests. - contains following features: - - ✓ assign role to user - - ✓ create space & internal alias to differentiate multiple spaces with the same name - - ✓ open space - - ✓ rename space - - ✓ change/set space subtitle - - ✓ change/set space description - - ✓ change/set space quota - - ✓ resources & existing resource actions - - ✗ change/set space image - - ✗ trash bin - - ✗ share - - ✗ link + once all needed features are there, split this into independent tests. + contains following features: + - ✓ assign role to user + - ✓ create space & internal alias to differentiate multiple spaces with the same name + - ✓ open space + - ✓ rename space + - ✓ change/set space subtitle + - ✓ change/set space description + - ✓ change/set space quota + - ✓ resources & existing resource actions + - ✗ change/set space image + - ✗ trash bin + - ✗ share + - ✗ link Given "Admin" creates following users | id | | Alice | @@ -47,10 +47,11 @@ Feature: spaces.personal | lorem.txt | folderPublic | | lorem.txt | folder_to_shared | - # borrowed from link.feature, all existing resource actions can be reused - When "Alice" creates a public link for the following resource using the sidebar panel - | resource | name | role | dateOfExpiration | password | - | folderPublic | team.1 | uploader | +5 days | 12345 | + And "Alice" creates a public link for the resource "folderPublic" using the sidebar panel + And "Alice" renames the most recently created public link of resource "folderPublic" to "team.1" + And "Alice" edits the public link named "team.1" of resource "folderPublic" changing role to "uploader" + And "Alice" sets the expiration date of the public link named "team.1" of resource "folderPublic" to "+5 days" + And "Alice" sets the password of the public link named "team.1" of resource "folderPublic" to "12345" # borrowed from share.feature When "Alice" shares the following resource using the sidebar panel @@ -65,16 +66,18 @@ Feature: spaces.personal And "Alice" updates the space "team.2" description to "management team - description" And "Alice" updates the space "team.2" quota to "500" - # borrowed from link.feature, all existing resource actions can be reused And "Alice" creates the following resources | resource | type | | folderPublic | folder | And "Alice" uploads the following resources | resource | to | | lorem.txt | folderPublic | - When "Alice" creates a public link for the following resource using the sidebar panel - | resource | name | role | dateOfExpiration | password | - | folderPublic | team.2 | uploader | +5 days | 54321 | + + And "Alice" creates a public link for the resource "folderPublic" using the sidebar panel + And "Alice" renames the most recently created public link of resource "folderPublic" to "team.2" + And "Alice" edits the public link named "team.2" of resource "folderPublic" changing role to "uploader" + And "Alice" sets the expiration date of the public link named "team.2" of resource "folderPublic" to "+5 days" + And "Alice" sets the password of the public link named "team.2" of resource "folderPublic" to "54321" # borrowed from link.feature, all existing resource actions can be reused When "Anonymous" opens the public link "team.1" @@ -91,10 +94,10 @@ Feature: spaces.personal | name | | folder_to_shared | And "Brian" renames the following resource - | resource | as | + | resource | as | | folder_to_shared/lorem.txt | lorem_new.txt | And "Brian" uploads the following resource - | resource | to | + | resource | to | | simple.pdf | folder_to_shared | And "Alice" navigates to the projects space page And "Alice" navigates to the project space "team.1" @@ -102,7 +105,7 @@ Feature: spaces.personal | resource | to | create_version | | PARENT/simple.pdf | folder_to_shared | true | When "Brian" restores following resources - | resource | to | version | + | resource | to | version | | simple.pdf | folder_to_shared | 1 | When "Alice" deletes the following resources | resource | diff --git a/tests/e2e/cucumber/steps/app-files/link.ts b/tests/e2e/cucumber/steps/app-files/link.ts index 6524c271039..3482de7b33c 100644 --- a/tests/e2e/cucumber/steps/app-files/link.ts +++ b/tests/e2e/cucumber/steps/app-files/link.ts @@ -1,31 +1,57 @@ -import { DataTable, When } from '@cucumber/cucumber' +import { When } from '@cucumber/cucumber' import { expect } from '@playwright/test' import { World } from '../../environment' import { objects } from '../../../support' When( - /^"([^"]*)" creates a public link for the following resource(s)? using the (sidebar panel|quick action)$/, + '{string} creates a public link for the resource {string} using the sidebar panel', + async function (this: World, stepUser: string, resource: string) { + const { page } = this.actorsEnvironment.getActor({ key: stepUser }) + const linkObject = new objects.applicationFiles.Link({ page }) + await linkObject.create({ + resource, + name: 'Link' + }) + } +) + +When( + '{string} renames the most recently created public link of resource {string} to {string}', + async function (this: World, stepUser: string, resource: string, newName: string): Promise { + const { page } = this.actorsEnvironment.getActor({ key: stepUser }) + const linkObject = new objects.applicationFiles.Link({ page }) + const linkName = await linkObject.changeName({ resource, newName }) + expect(newName).toBe(linkName) + } +) + +When( + '{string} sets the expiration date of the public link named {string} of resource {string} to {string}', async function ( this: World, stepUser: string, - _: string, - actionType: string, - stepTable: DataTable - ) { + linkName: string, + resource: string, + expireDate: string + ): Promise { + const { page } = this.actorsEnvironment.getActor({ key: stepUser }) + const linkObject = new objects.applicationFiles.Link({ page }) + await linkObject.addExpiration({ resource, linkName, expireDate }) + } +) + +When( + '{string} sets the password of the public link named {string} of resource {string} to {string}', + async function ( + this: World, + stepUser: string, + linkName: string, + resource: string, + newPassword: string + ): Promise { const { page } = this.actorsEnvironment.getActor({ key: stepUser }) const linkObject = new objects.applicationFiles.Link({ page }) - const shareInfo = stepTable.hashes() - for (const linkShare of shareInfo) { - const { resource, name, role, dateOfExpiration, password } = linkShare - await linkObject.create({ - resource, - name, - role, - dateOfExpiration, - password, - via: actionType === 'quick action' ? 'QUICK_ACTION' : 'SIDEBAR_PANEL' - }) - } + await linkObject.addPassword({ resource, linkName, newPassword }) } ) diff --git a/tests/e2e/cucumber/steps/app-files/page/public.ts b/tests/e2e/cucumber/steps/app-files/page/public.ts index 1d3340d7ae3..a76c922fdde 100644 --- a/tests/e2e/cucumber/steps/app-files/page/public.ts +++ b/tests/e2e/cucumber/steps/app-files/page/public.ts @@ -42,7 +42,7 @@ When( } const { page } = actor - const { url } = this.linksEnvironment.getLink({ key: name }) + const { url } = this.linksEnvironment.getLink({ name }) const pageObject = new objects.applicationFiles.page.Public({ page }) await pageObject.open({ url }) } diff --git a/tests/e2e/support/environment/link.ts b/tests/e2e/support/environment/link.ts index 31318eb8819..5a5a3260274 100644 --- a/tests/e2e/support/environment/link.ts +++ b/tests/e2e/support/environment/link.ts @@ -2,21 +2,26 @@ import { Link } from '../types' import { linkStore } from '../store' export class LinksEnvironment { - getLink({ key }: { key: string }): Link { - if (!linkStore.has(key)) { - throw new Error(`link with key '${key}' not found`) + getLink({ name }: { name: string }): Link { + if (!linkStore.has(name)) { + throw new Error(`link with name '${name}' not found`) } + return linkStore.get(name) + } - return linkStore.get(key) + updateLinkName({ key, link }: { key: string; link: Link }): any { + if (!linkStore.has(key)) { + throw new Error(`link with name '${key}' not found`) + } + linkStore.set(link.name, link) + linkStore.delete(key) } createLink({ key, link }: { key: string; link: Link }): Link { if (linkStore.has(key)) { throw new Error(`link with key '${key}' already exists`) } - linkStore.set(key, link) - return link } diff --git a/tests/e2e/support/objects/app-files/link/actions.ts b/tests/e2e/support/objects/app-files/link/actions.ts index ca2a0ac903f..cee3de63bf7 100644 --- a/tests/e2e/support/objects/app-files/link/actions.ts +++ b/tests/e2e/support/objects/app-files/link/actions.ts @@ -9,10 +9,26 @@ export interface createLinkArgs { page: Page resource: string name: string - role: string - dateOfExpiration: string - password: string - via: 'SIDEBAR_PANEL' | 'QUICK_ACTION' +} + +export type changeNameArgs = { + page: Page + resource: string + newName: string +} + +export type addExpirationArgs = { + page: Page + resource: string + linkName: string + expireDate: string +} + +export type addPasswordArgs = { + page: Page + resource: string + linkName: string + newPassword: string } export type changeRoleArgs = { @@ -28,17 +44,13 @@ export type deleteLinkArgs = { name: string } -const publicLinkSetNameInputField = '#oc-files-file-link-name' -const publicLinkSelectRolesButton = '#files-file-link-role-button' const publicLinkSetRoleButton = `#files-role-%s` -const publicLinkExpiryDate = '#oc-files-file-link-expire-date' -const publicLinkSetPasswordInputField = '#oc-files-file-link-password' +const linkExpiryDatepicker = '.link-expiry-picker' const publicLinkEditRoleButton = `//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "link-details")]/` + `div/button[contains(@class, "edit-public-link-role-dropdown-toggl")]` -const publicLinkQuickActionButton = `//*[@data-test-resource-name="%s"]/ancestor::tr//button[contains(@class, "files-quick-action-collaborators")]` const addPublicLinkButton = '#files-file-link-add' -const savePublicLinkButton = '#oc-files-file-link-create' +const getMostRecentLink = '//div[@id="oc-files-file-link"]//ul/li[1]' const publicLink = `//ul/li/div/h4[contains(text(),'%s')]/following-sibling::div//p` const publicLinkCurrentRole = '//button[contains(@class,"edit-public-link-role-dropdown-toggl")]//span[contains(@class,"oc-invisible-sr")]' @@ -46,61 +58,32 @@ const linkUpdateDialog = '//div[contains(@class,"oc-notification-message-title") const editPublicLinkButton = `//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]` + `//ancestor::li//div[contains(@class, "details-buttons")]//button[contains(@class, "edit-drop-trigger")]` +const editPublicLinkRenameButton = '//button[text()="Rename"]' +const editPublicLinkSetExpirationButton = '//button[text()="Add expiration date"]' +const editPublicLinkAddPasswordButton = '//button[text()="Add password"]' +const editPublicLinkInput = '.oc-modal-body input.oc-text-input' +const editPublicLinkRenameConfirm = '.oc-modal-body-actions-confirm' const deleteLinkButton = `//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]` + `//ancestor::li//div[contains(@class, "details-buttons")]//button[text()="Delete link"]` const confirmDeleteButton = `//button[contains(@class,"oc-modal-body-actions-confirm") and text()="Delete"]` -const fillPublicLink = async (page, name, role, dateOfExpiration, password): Promise => { - if (name) { - await page.locator(publicLinkSetNameInputField).fill(name) - } - - if (role) { - await page.locator(publicLinkSelectRolesButton).click() - await page.locator(util.format(publicLinkSetRoleButton, role)).click() - } - - if (dateOfExpiration) { - const newExpiryDate = getActualExpiryDate( - dateOfExpiration.toLowerCase().match(/[dayrmonthwek]+/)[0] as any, - dateOfExpiration - ) - - await page.locator(publicLinkExpiryDate).evaluate( - (datePicker: any, { newExpiryDate }): any => { - datePicker.__vue__.updateValue(newExpiryDate) - }, - { newExpiryDate } - ) - } - if (password) { - await page.locator(publicLinkSetPasswordInputField).fill(password) - } -} - export const createLink = async (args: createLinkArgs): Promise => { - const { page, resource, name, role, dateOfExpiration, password, via } = args + const { page, resource } = args const resourcePaths = resource.split('/') const resourceName = resourcePaths.pop() if (resourcePaths.length) { await clickResource({ page: page, path: resourcePaths.join('/') }) } - switch (via) { - case 'QUICK_ACTION': - await page.locator(util.format(publicLinkQuickActionButton, resourceName)).click() - break - - case 'SIDEBAR_PANEL': - await sidebar.open({ page: page, resource: resourceName }) - await sidebar.openPanel({ page: page, name: 'sharing' }) - break - } + await sidebar.open({ page: page, resource: resourceName }) + await sidebar.openPanel({ page: page, name: 'sharing' }) await page.locator(addPublicLinkButton).click() - await fillPublicLink(page, name, role, dateOfExpiration, password) - await page.locator(savePublicLinkButton).click() - return await page.locator(util.format(publicLink, name)).textContent() + // const message = await page.locator(linkUpdateDialog).textContent() + // expect(message.trim()).toBe('Link was created successfully') + // const linkId = await page.getAttribute(getMostRecentLink, 'data-testid') + // return linkId.replace('files-link-id-', '') + return await page.locator(util.format(publicLink, 'Link')).textContent() } export const changeRole = async (args: changeRoleArgs): Promise => { @@ -119,6 +102,66 @@ export const changeRole = async (args: changeRoleArgs): Promise => { return await page.locator(publicLinkCurrentRole).textContent() } +export const changeName = async (args: changeNameArgs): Promise => { + const { page, resource, newName } = args + const resourcePaths = resource.split('/') + const resourceName = resourcePaths.pop() + if (resourcePaths.length) { + await clickResource({ page: page, path: resourcePaths.join('/') }) + } + await sidebar.open({ page: page, resource: resourceName }) + await sidebar.openPanel({ page: page, name: 'sharing' }) + await page.locator(util.format(editPublicLinkButton, 'Link')).click() + await page.locator(editPublicLinkRenameButton).click() + await page.locator(editPublicLinkInput).fill(newName) + await page.locator(editPublicLinkRenameConfirm).click() + const message = await page.locator(linkUpdateDialog).textContent() + expect(message.trim()).toBe('Link was updated successfully') + return await page.locator(getMostRecentLink + '//h4').textContent() +} + +export const addPassword = async (args: addPasswordArgs): Promise => { + const { page, resource, linkName, newPassword } = args + const resourcePaths = resource.split('/') + const resourceName = resourcePaths.pop() + if (resourcePaths.length) { + await clickResource({ page: page, path: resourcePaths.join('/') }) + } + await sidebar.open({ page: page, resource: resourceName }) + await sidebar.openPanel({ page: page, name: 'sharing' }) + await page.locator(util.format(editPublicLinkButton, linkName)).click() + await page.locator(editPublicLinkAddPasswordButton).click() + await page.locator(editPublicLinkInput).fill(newPassword) + await page.locator(editPublicLinkRenameConfirm).click() + const message = await page.locator(linkUpdateDialog).textContent() + expect(message.trim()).toBe('Link was updated successfully') +} + +export const addExpiration = async (args: addExpirationArgs): Promise => { + const { page, resource, linkName, expireDate } = args + const resourcePaths = resource.split('/') + const resourceName = resourcePaths.pop() + if (resourcePaths.length) { + await clickResource({ page: page, path: resourcePaths.join('/') }) + } + await sidebar.open({ page: page, resource: resourceName }) + await sidebar.openPanel({ page: page, name: 'sharing' }) + await page.locator(util.format(editPublicLinkButton, linkName)).click() + await page.locator(editPublicLinkSetExpirationButton).click() + + const newExpiryDate = getActualExpiryDate( + expireDate.toLowerCase().match(/[dayrmonthwek]+/)[0] as any, + expireDate + ) + + await page.locator(linkExpiryDatepicker).evaluate( + (datePicker: any, { newExpiryDate }): any => { + datePicker.__vue__.updateValue(newExpiryDate) + }, + { newExpiryDate } + ) +} + export const deleteLink = async (args: deleteLinkArgs): Promise => { const { page, resourceName, name } = args await sidebar.open({ page: page, resource: resourceName }) diff --git a/tests/e2e/support/objects/app-files/link/index.ts b/tests/e2e/support/objects/app-files/link/index.ts index 5f83826f2aa..6d5d2f344e3 100644 --- a/tests/e2e/support/objects/app-files/link/index.ts +++ b/tests/e2e/support/objects/app-files/link/index.ts @@ -2,6 +2,12 @@ import { Page } from 'playwright' import { createLink, createLinkArgs, + changeName, + changeNameArgs, + addExpiration, + addExpirationArgs, + addPassword, + addPasswordArgs, changeRole, changeRoleArgs, deleteLinkArgs, @@ -24,12 +30,40 @@ export class Link { this.#linksEnvironment.createLink({ key: args.name, - link: { name: args.name, url, password: args.password } + link: { name: args.name, url } }) await this.#page.goto(startUrl) } + async changeName(args: Omit): Promise { + const startUrl = this.#page.url() + const name = await changeName({ page: this.#page, ...args }) + const currentLink = this.#linksEnvironment.getLink({ name: 'Link' }) + + this.#linksEnvironment.updateLinkName({ + key: currentLink.name, + link: { ...currentLink, name } + }) + await this.#page.goto(startUrl) + return name + } + + async addExpiration(args: Omit): Promise { + const startUrl = this.#page.url() + // const name = + await addExpiration({ page: this.#page, ...args }) + await this.#page.goto(startUrl) + // return + } + + async addPassword(args: Omit): Promise { + const startUrl = this.#page.url() + // const name = + await addPassword({ page: this.#page, ...args }) + await this.#page.goto(startUrl) + } + async changeRole(args: Omit): Promise { const startUrl = this.#page.url() const role = await changeRole({ page: this.#page, ...args })