From 5b916fb0b542a954258a59589501e6c6def0b556 Mon Sep 17 00:00:00 2001 From: Pascal Wengerter Date: Mon, 25 Apr 2022 16:59:51 +0200 Subject: [PATCH 1/2] Bump oCIS commit id, ownCloud SDK & webdav dependency, amend ODS changelog --- .drone.env | 2 +- .../enhancement-bump-ods-13.1.0-rc.5 | 5 -- changelog/unreleased/enhancement-update-ods | 19 +++++++ packages/web-runtime/package.json | 4 +- yarn.lock | 57 +++++++++++-------- 5 files changed, 55 insertions(+), 32 deletions(-) delete mode 100644 changelog/unreleased/enhancement-bump-ods-13.1.0-rc.5 create mode 100644 changelog/unreleased/enhancement-update-ods diff --git a/.drone.env b/.drone.env index a49fd57fa41..fe42427922a 100644 --- a/.drone.env +++ b/.drone.env @@ -1,3 +1,3 @@ # The version of OCIS to use in pipelines that test against OCIS -OCIS_COMMITID=726604951db1a1921697a6b493e661f451e67755 +OCIS_COMMITID=4202a99600ef15bc7a6bd91b011074775c19a24a OCIS_BRANCH=master diff --git a/changelog/unreleased/enhancement-bump-ods-13.1.0-rc.5 b/changelog/unreleased/enhancement-bump-ods-13.1.0-rc.5 deleted file mode 100644 index f9e2c3dde0c..00000000000 --- a/changelog/unreleased/enhancement-bump-ods-13.1.0-rc.5 +++ /dev/null @@ -1,5 +0,0 @@ -Enhancement: Bump ODS to 13.1.0 RC.5 - -We've bumped the version of the design system to v13.1.0-rc.5 - -https://github.com/owncloud/web/pull/6750 \ No newline at end of file diff --git a/changelog/unreleased/enhancement-update-ods b/changelog/unreleased/enhancement-update-ods new file mode 100644 index 00000000000..2ce6fb68119 --- /dev/null +++ b/changelog/unreleased/enhancement-update-ods @@ -0,0 +1,19 @@ +Enhancement: Update ODS to v13.1.0-rc.5 + +We updated the ownCloud Design System to version 13.1.0-rc.5. Please refer to the full changelog in the ODS release (linked) for more details. Summary: + +- Enhancement - Add isFileExtensionDisplayed property: https://github.com/owncloud/owncloud-design-system/pull/2087 +- Enhancement - OcModal input type: https://github.com/owncloud/owncloud-design-system/pull/2077 +- Enhancement - Add OcContextualHelper: https://github.com/owncloud/owncloud-design-system/pull/2064 +- Enhancement - Add selection range for OcModal and OcTextInput: https://github.com/owncloud/owncloud-design-system/pull/2061 +- Enhancement - Replace deprecated String.prototype.substr(): https://github.com/owncloud/owncloud-design-system/pull/2059 +- Enhancement - Redesign OcGhostElement: https://github.com/owncloud/owncloud-design-system/pull/2049 +- Enhancement - Export package members: https://github.com/owncloud/owncloud-design-system/pull/2048 +- Enhancement - Make OcResource inline-flex: https://github.com/owncloud/owncloud-design-system/pull/2041 +- Bugfix - Disabled textarea color contrast in darkmode: https://github.com/owncloud/owncloud-design-system/pull/2055 +- Bugfix - OcTextInput: Fix event handlers in loops: https://github.com/owncloud/owncloud-design-system/pull/2054 + +https://github.com/owncloud/web/pull/6749 +https://github.com/owncloud/web/pull/6750 +https://github.com/owncloud/owncloud-design-system/releases/tag/v13.1.0-rc.5 + diff --git a/packages/web-runtime/package.json b/packages/web-runtime/package.json index 267b99110ab..4252ad07ba0 100644 --- a/packages/web-runtime/package.json +++ b/packages/web-runtime/package.json @@ -4,7 +4,7 @@ "description": "ownCloud web runtime", "license": "AGPL-3.0", "dependencies": { - "@fortawesome/fontawesome-free": "^5.15.4", + "@fortawesome/fontawesome-free": "^6.1.1", "@popperjs/core": "^2.4.0", "@vue/composition-api": "^1.4.9", "axios": "^0.26.1", @@ -51,7 +51,7 @@ "vuex-persist": "3.1.3", "vuex-router-sync": "^5.0.0", "web-pkg": "*", - "webdav": "4.8.0", + "webdav": "4.9.0", "webfontloader": "^1.6.28", "xml-js": "^1.6.11" }, diff --git a/yarn.lock b/yarn.lock index 20f78b08b56..e71f9a27c16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1657,10 +1657,10 @@ __metadata: languageName: node linkType: hard -"@fortawesome/fontawesome-free@npm:^5.15.4": - version: 5.15.4 - resolution: "@fortawesome/fontawesome-free@npm:5.15.4" - checksum: 32281c3df4075290d9a96dfc22f72fadb3da7055d4117e48d34046b8c98032a55fa260ae351b0af5d6f6fb57a2f5d79a4abe52af456da35195f7cb7dda27b4a2 +"@fortawesome/fontawesome-free@npm:^6.1.1": + version: 6.1.1 + resolution: "@fortawesome/fontawesome-free@npm:6.1.1" + checksum: adb3c71e0d9e60488401a193cfd1925ad100935ae41c88d3969937b59c182289ecc6f8c0835f0a3fb9cc215348a047a39536e918247381774baf7dcebef94b64 languageName: node linkType: hard @@ -3417,15 +3417,6 @@ __metadata: languageName: node linkType: hard -"axios@npm:^0.24.0": - version: 0.24.0 - resolution: "axios@npm:0.24.0" - dependencies: - follow-redirects: ^1.14.4 - checksum: 468cf496c08a6aadfb7e699bebdac02851e3043d4e7d282350804ea8900e30d368daa6e3cd4ab83b8ddb5a3b1e17a5a21ada13fc9cebd27b74828f47a4236316 - languageName: node - linkType: hard - "axios@npm:^0.26.1": version: 0.26.1 resolution: "axios@npm:0.26.1" @@ -3816,6 +3807,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: ^1.0.0 + checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + "braces@npm:^3.0.1, braces@npm:~3.0.2": version: 3.0.2 resolution: "braces@npm:3.0.2" @@ -6459,7 +6459,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.8": +"follow-redirects@npm:^1.14.8": version: 1.14.9 resolution: "follow-redirects@npm:1.14.9" peerDependenciesMeta: @@ -9081,6 +9081,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^5.0.1": + version: 5.0.1 + resolution: "minimatch@npm:5.0.1" + dependencies: + brace-expansion: ^2.0.1 + checksum: b34b98463da4754bc526b244d680c69d4d6089451ebe512edaf6dd9eeed0279399cfa3edb19233513b8f830bf4bfcad911dddcdf125e75074100d52f724774f0 + languageName: node + linkType: hard + "minimist@npm:0.0.8": version: 0.0.8 resolution: "minimist@npm:0.0.8" @@ -13374,7 +13383,7 @@ __metadata: languageName: node linkType: hard -"url-parse@npm:^1.5.3, url-parse@npm:^1.5.7": +"url-parse@npm:^1.5.10, url-parse@npm:^1.5.7": version: 1.5.10 resolution: "url-parse@npm:1.5.10" dependencies: @@ -13805,7 +13814,7 @@ __metadata: version: 0.0.0-use.local resolution: "web-runtime@workspace:packages/web-runtime" dependencies: - "@fortawesome/fontawesome-free": ^5.15.4 + "@fortawesome/fontawesome-free": ^6.1.1 "@popperjs/core": ^2.4.0 "@types/luxon": ^2.0.8 "@types/semver": ^7.3.8 @@ -13854,29 +13863,29 @@ __metadata: vuex-persist: 3.1.3 vuex-router-sync: ^5.0.0 web-pkg: "*" - webdav: 4.8.0 + webdav: 4.9.0 webfontloader: ^1.6.28 xml-js: ^1.6.11 languageName: unknown linkType: soft -"webdav@npm:4.8.0": - version: 4.8.0 - resolution: "webdav@npm:4.8.0" +"webdav@npm:4.9.0": + version: 4.9.0 + resolution: "webdav@npm:4.9.0" dependencies: - axios: ^0.24.0 + axios: ^0.26.1 base-64: ^1.0.0 fast-xml-parser: ^3.19.0 he: ^1.2.0 hot-patcher: ^0.5.0 layerr: ^0.1.2 md5: ^2.3.0 - minimatch: ^3.0.4 + minimatch: ^5.0.1 nested-property: ^4.0.0 path-posix: ^1.0.0 url-join: ^4.0.1 - url-parse: ^1.5.3 - checksum: be3f04c359b8f8800febfd18ca5e05258503180f3da7f804630a09bb19b69c0a558182f9bc427daee23785ddd0066a916f740c775de1ca3762981b09f2f1478a + url-parse: ^1.5.10 + checksum: eb2c65bdc7d3a2037d3c27b2f5624421c2312ecccd1bf9edbfa9b99a7704973da9f046e5395b370cadea6502ad39da9cd2d15c85b425d0a92af8a24015e0fb5b languageName: node linkType: hard From e8828065418a36a43b4feb2c8f4390a61c835a94 Mon Sep 17 00:00:00 2001 From: Pascal Wengerter Date: Tue, 26 Apr 2022 13:42:39 +0200 Subject: [PATCH 2/2] Redesign link list components to include inline edit & event driven architecture Address code review Fix remaining acceptance tests Remove unused error boolean & alert on CreateLink form component Check for ReadWriteDelete password enforcement Include #6808 --- .../bugfix-public-link-password-enforcement | 7 + .../enhancement-redesign-link-sharing | 5 + packages/web-app-files/src/App.vue | 2 +- .../SideBar/Shared/CopyToClipboardButton.vue | 3 +- .../components/SideBar/Shares/FileLinks.vue | 358 ++++++++--- .../SideBar/Shares/Links/CreateForm.vue | 246 ++++++++ .../SideBar/Shares/Links/DetailsAndEdit.vue | 498 +++++++++++++++ .../SideBar/Shares/Links/NameAndCopy.vue | 64 ++ .../Shares/PublicLinks/LinkActions.vue | 104 ---- .../SideBar/Shares/PublicLinks/LinkEdit.vue | 443 -------------- .../SideBar/Shares/PublicLinks/LinkInfo.vue | 178 ------ .../SideBar/Shares/PublicLinks/ListItem.vue | 47 -- .../web-app-files/src/helpers/resources.ts | 2 +- packages/web-app-files/src/quickActions.js | 6 +- packages/web-app-files/src/store/mutations.js | 25 - packages/web-app-files/src/store/state.js | 11 - .../specs/linkExpirationDate.spec.js | 38 +- .../SideBar/Shares/FileLinks.spec.js | 74 +-- .../SideBar/Shares/Links/CreateForm.spec.js | 3 + .../Shares/Links/DetailsAndEdit.spec.js | 86 +++ .../SideBar/Shares/Links/NameAndCopy.spec.js | 36 ++ .../__snapshots__/DetailsAndEdit.spec.js.snap | 63 ++ .../__snapshots__/NameAndCopy.spec.js.snap | 14 + .../Shares/PublicLinks/LinkActions.spec.js | 147 ----- .../Shares/PublicLinks/LinkEdit.spec.js | 578 ------------------ .../Shares/PublicLinks/LinkInfo.spec.js | 235 ------- .../Shares/PublicLinks/ListItem.spec.js | 53 -- .../__snapshots__/LinkEdit.spec.js.snap | 5 - packages/web-runtime/package.json | 2 +- ...-failures-with-ocis-server-ocis-storage.md | 2 +- .../publicLinkCreate.feature | 48 +- .../publicLinkEdit.feature | 78 +-- .../shareByPublicLinkDifferentRoles.feature | 8 +- .../shareByPublicLinkExpiringLinks.feature | 13 +- .../FilesPageElement/publicLinksDialog.js | 233 ++++++- .../stepDefinitions/publicLinkContext.js | 62 +- .../support/objects/app-files/link/actions.ts | 2 +- yarn.lock | 10 +- 38 files changed, 1661 insertions(+), 2128 deletions(-) create mode 100644 changelog/unreleased/bugfix-public-link-password-enforcement create mode 100644 changelog/unreleased/enhancement-redesign-link-sharing create mode 100644 packages/web-app-files/src/components/SideBar/Shares/Links/CreateForm.vue create mode 100644 packages/web-app-files/src/components/SideBar/Shares/Links/DetailsAndEdit.vue create mode 100644 packages/web-app-files/src/components/SideBar/Shares/Links/NameAndCopy.vue delete mode 100644 packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkActions.vue delete mode 100644 packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkEdit.vue delete mode 100644 packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkInfo.vue delete mode 100644 packages/web-app-files/src/components/SideBar/Shares/PublicLinks/ListItem.vue create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/Links/CreateForm.spec.js create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/Links/DetailsAndEdit.spec.js create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/Links/NameAndCopy.spec.js create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/DetailsAndEdit.spec.js.snap create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/NameAndCopy.spec.js.snap delete mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkActions.spec.js delete mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkEdit.spec.js delete mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkInfo.spec.js delete mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/ListItem.spec.js delete mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/__snapshots__/LinkEdit.spec.js.snap diff --git a/changelog/unreleased/bugfix-public-link-password-enforcement b/changelog/unreleased/bugfix-public-link-password-enforcement new file mode 100644 index 00000000000..22e39166c67 --- /dev/null +++ b/changelog/unreleased/bugfix-public-link-password-enforcement @@ -0,0 +1,7 @@ +Bugfix: Password enforcement for public links + +Password enforcement for public links, which can be adjusted on a per-role basis, wasn't properly reflected in the UI. +We have made the necessary adjustments to only enforce passwords for public links with the permissions that require a password according to the backend settings. + +https://github.com/owncloud/web/issues/6323 +https://github.com/owncloud/web/pull/6749 diff --git a/changelog/unreleased/enhancement-redesign-link-sharing b/changelog/unreleased/enhancement-redesign-link-sharing new file mode 100644 index 00000000000..503b7199567 --- /dev/null +++ b/changelog/unreleased/enhancement-redesign-link-sharing @@ -0,0 +1,5 @@ +Enhancement: Redesign link sharing + +We have redesigned the public link list in the right sidebar. Links now can be edited in-line and have a similiar look-and-feel to people and group shares. + +https://github.com/owncloud/web/pull/6749 diff --git a/packages/web-app-files/src/App.vue b/packages/web-app-files/src/App.vue index 57036b40034..fb8aae9e5fd 100644 --- a/packages/web-app-files/src/App.vue +++ b/packages/web-app-files/src/App.vue @@ -13,7 +13,7 @@ id="files-sidebar" ref="filesSidebar" tabindex="-1" - class="oc-width-1-1 oc-width-1-3@m oc-width-1-4@xl" + class="oc-width-1-1 oc-width-2-5@m oc-width-1-4@xl" :sidebar-active-panel="sidebarActivePanel" @beforeDestroy="focusSideBar" @mounted="focusSideBar" diff --git a/packages/web-app-files/src/components/SideBar/Shared/CopyToClipboardButton.vue b/packages/web-app-files/src/components/SideBar/Shared/CopyToClipboardButton.vue index b03f85d4c34..ba777983161 100644 --- a/packages/web-app-files/src/components/SideBar/Shared/CopyToClipboardButton.vue +++ b/packages/web-app-files/src/components/SideBar/Shared/CopyToClipboardButton.vue @@ -3,6 +3,7 @@ v-oc-tooltip="label" :aria-label="label" appearance="raw" + :variation="copied ? 'success' : 'passive'" @click="copyValueToClipboard" > @@ -84,7 +85,7 @@ export default { diff --git a/packages/web-app-files/src/components/SideBar/Shares/Links/NameAndCopy.vue b/packages/web-app-files/src/components/SideBar/Shares/Links/NameAndCopy.vue new file mode 100644 index 00000000000..bbe82bbc163 --- /dev/null +++ b/packages/web-app-files/src/components/SideBar/Shares/Links/NameAndCopy.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkActions.vue b/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkActions.vue deleted file mode 100644 index 8af8c5c9e84..00000000000 --- a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkActions.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - diff --git a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkEdit.vue b/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkEdit.vue deleted file mode 100644 index 010233b3d34..00000000000 --- a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkEdit.vue +++ /dev/null @@ -1,443 +0,0 @@ - - diff --git a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkInfo.vue b/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkInfo.vue deleted file mode 100644 index 0a48d23eed6..00000000000 --- a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/LinkInfo.vue +++ /dev/null @@ -1,178 +0,0 @@ - - - diff --git a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/ListItem.vue b/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/ListItem.vue deleted file mode 100644 index 569f25da1bd..00000000000 --- a/packages/web-app-files/src/components/SideBar/Shares/PublicLinks/ListItem.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - - - diff --git a/packages/web-app-files/src/helpers/resources.ts b/packages/web-app-files/src/helpers/resources.ts index 36d299ca08c..2c0d78acfb8 100644 --- a/packages/web-app-files/src/helpers/resources.ts +++ b/packages/web-app-files/src/helpers/resources.ts @@ -442,7 +442,7 @@ function _buildLink(link): Share { permissions: link.permissions, description, stime: link.stime, - name: typeof link.name === 'string' ? link.name : '', + name: typeof link.name === 'string' ? link.name : (link.token as string), password: !!(link.share_with && link.share_with_displayname), expiration: typeof link.expiration === 'string' diff --git a/packages/web-app-files/src/quickActions.js b/packages/web-app-files/src/quickActions.js index b442449d72c..c9fce52a031 100644 --- a/packages/web-app-files/src/quickActions.js +++ b/packages/web-app-files/src/quickActions.js @@ -2,18 +2,20 @@ import { DateTime } from 'luxon' import copyToClipboard from 'copy-to-clipboard' export function createPublicLink(ctx) { - // FIXME: Translate name const params = { name: ctx.$gettext('Quick action link'), permissions: 1 } const capabilities = ctx.store.state.user.capabilities const expirationDate = capabilities.files_sharing.public.expire_date - if (expirationDate.enabled) { + if (expirationDate.enforced) { params.expireDate = DateTime.now() .plus({ days: parseInt(expirationDate.days, 10) }) .endOf('day') .toISO() } + // needs check for enforced password for default role (viewer?) + // and concept to what happens if it is enforced + if (ctx.storageId) { params.spaceRef = `${ctx.storageId}${ctx.item.path}` } diff --git a/packages/web-app-files/src/store/mutations.js b/packages/web-app-files/src/store/mutations.js index 2396e82beef..af49a67a8fc 100644 --- a/packages/web-app-files/src/store/mutations.js +++ b/packages/web-app-files/src/store/mutations.js @@ -1,6 +1,5 @@ import Vue from 'vue' import pickBy from 'lodash-es/pickBy' -import { DateTime } from 'luxon' import { set, has } from 'lodash-es' import { getIndicators } from '../helpers/statusIndicators' import { renameResource } from '../helpers/resources' @@ -243,30 +242,6 @@ export default { state.versions = versions }, - TRIGGER_PUBLIC_LINK_EDIT(state, link) { - // Adjust link for the edit - link = { - id: link.id, - name: link.name, - permissions: parseInt(link.permissions, 10), - hasPassword: link.password, - expireDate: - link.expiration !== null ? DateTime.fromISO(link.expiration).endOf('day').toISO() : null - } - - state.publicLinkInEdit = link - }, - - TRIGGER_PUBLIC_LINK_CREATE(state, { name, expireDate }) { - state.publicLinkInEdit = { - id: null, - name, - permissions: 1, - hasPassword: false, - expireDate - } - }, - LOAD_INDICATORS(state) { for (const resource of state.files) { const indicators = getIndicators(resource, state.sharesTree) diff --git a/packages/web-app-files/src/store/state.js b/packages/web-app-files/src/store/state.js index 2e33b8588c3..69fc5bb5393 100644 --- a/packages/web-app-files/src/store/state.js +++ b/packages/web-app-files/src/store/state.js @@ -34,17 +34,6 @@ export default { uploaded: [], actionsInProgress: [], - /** - * Public links - */ - publicLinkInEdit: { - id: null, - name: '', - permissions: 1, - hasPassword: false, - expireDate: null - }, - /** * View settings */ diff --git a/packages/web-app-files/tests/integration/specs/linkExpirationDate.spec.js b/packages/web-app-files/tests/integration/specs/linkExpirationDate.spec.js index a6999fc5058..3804e7e133d 100644 --- a/packages/web-app-files/tests/integration/specs/linkExpirationDate.spec.js +++ b/packages/web-app-files/tests/integration/specs/linkExpirationDate.spec.js @@ -51,7 +51,7 @@ const existingShares = [ share_type: 3, uid_owner: 'alice', displayname_owner: 'alice', - permissions: 16, + permissions: '1', stime: new Date().getTime(), expiration: DateTime.fromJSDate(getDateInFuture(2)).toFormat('yyyy-MM-dd HH:mm:ss'), uid_file_owner: 'alice', @@ -79,7 +79,7 @@ describe('Users can set expiration date when sharing via public link', () => { }) test('user can set a new expiration date', async () => { const component = renderComponent() - const { findByTestId, baseElement, getByTestId, findByText, queryByTestId } = component + const { findByTestId, baseElement, getByTestId, queryByTestId } = component const addBtn = await findByTestId('files-link-add-btn') expect(addBtn).toBeVisible() @@ -93,8 +93,6 @@ describe('Users can set expiration date when sharing via public link', () => { const newDate = getDateInFuture(2) await navigateToDate(newDate, component) - expect(await findByText('Expires in 2 days')).toBeVisible() - const shareBtn = getByTestId('new-files-link-btn') expect(shareBtn).toBeVisible() await fireEvent.click(shareBtn) @@ -106,7 +104,9 @@ describe('Users can set expiration date when sharing via public link', () => { expect(link).toBeVisible() expect( - within(getByTestId('files-link-id-1-expiration-date')).getByText('Expires in 2 days') + within(getByTestId('files-link-id-1')).getByLabelText('Expires in in 2 days', { + exact: false + }) ).toBeVisible() }) @@ -122,7 +122,7 @@ describe('Users can set expiration date when sharing via public link', () => { } } }) - const { findByTestId, getByTestId, findByText, queryByTestId } = component + const { findByTestId, getByTestId } = component const link = await findByTestId('files-link-id-1') expect(link).toBeVisible() @@ -131,23 +131,17 @@ describe('Users can set expiration date when sharing via public link', () => { expect(editBtn).toBeVisible() await fireEvent.click(editBtn) - expect(getByTestId('recipient-datepicker')).toBeVisible() - await fireEvent.click(getByTestId('recipient-datepicker-btn')) + const editExpiryDateBtn = getByTestId('files-link-id-1-edit-edit-expiration') + expect(editExpiryDateBtn).toBeVisible() + await fireEvent.click(editExpiryDateBtn) const newDate = getDateInFuture(4) await navigateToDate(newDate, component) - expect(await findByText('Expires in 4 days')).toBeVisible() - - const shareBtn = getByTestId('save-files-link-btn') - expect(shareBtn).toBeVisible() - await fireEvent.click(shareBtn) - await waitFor(() => { - return expect(queryByTestId('files-link-being-saved')).toBe(null) - }) - expect( - within(getByTestId('files-link-id-1-expiration-date')).getByText('Expires in 4 days') + within(getByTestId('files-link-id-1')).getByLabelText('Expires in in 4 days', { + exact: false + }) ).toBeVisible() }) @@ -172,15 +166,11 @@ describe('Users can set expiration date when sharing via public link', () => { expect(editBtn).toBeVisible() await fireEvent.click(editBtn) - const removeExpiryDateBtn = getByTestId('files-link-remove-expiration-date') + const removeExpiryDateBtn = getByTestId('files-link-id-1-edit-remove-expiration') expect(removeExpiryDateBtn).toBeVisible() await fireEvent.click(removeExpiryDateBtn) - - const shareBtn = getByTestId('save-files-link-btn') - expect(shareBtn).toBeVisible() - await fireEvent.click(shareBtn) await waitFor(() => { - return expect(queryByTestId('files-link-being-saved')).toBe(null) + return expect(queryByTestId('files-link-id-1-edit-remove-expiration')).toBe(null) }) expect(within(link).queryByTestId('files-link-id-1-expiration-date')).toBe(null) diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js index 507bc1ed260..c8096ee8445 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js @@ -29,14 +29,16 @@ const defaultLinksList = [ indirect: false, name: 'public link 1', url: 'some-link-1', - path: '/file-1.txt' + path: '/file-1.txt', + permissions: '1' }, { id: '2', indirect: true, name: 'public link 2', url: 'some-link-2', - path: '/file-2.txt' + path: '/file-2.txt', + permissions: '1' } ] @@ -47,8 +49,8 @@ const selectors = { linkPrivate: '.oc-files-private-link-item' } -const listItemStubSelector = 'list-item-stub' -const linkEditStubSelector = 'link-edit-stub' +const linkListItemNameAndCopy = 'name-and-copy-stub' +const linkListItemDetailsAndEdit = 'details-and-edit-stub' const ocLoaderStubSelector = 'oc-loader-stub' describe('FileLinks', () => { @@ -58,9 +60,12 @@ describe('FileLinks', () => { const wrapper = getShallowWrapper(store) it('should render a list of links', () => { - const linkListItems = wrapper.findAll(listItemStubSelector) + const linkListItems = wrapper.findAll(linkListItemNameAndCopy) + const linkListItemsDetails = wrapper.findAll(linkListItemDetailsAndEdit) expect(linkListItems.length).toBe(2) + expect(linkListItemsDetails.length).toBe(2) + expect(linkListItems.at(0).props().link).toMatchObject({ id: '1', indirect: false, @@ -80,11 +85,9 @@ describe('FileLinks', () => { }) }) - it('should show the "no results" message if no links are provided', () => { + it('should not render link list if no links are provided', () => { const wrapper = getShallowWrapper(createStore({ links: [] })) - - expect(wrapper.find(selectors.linkNoResults).exists()).toBeTruthy() - expect(wrapper.find(selectors.linkNoResults).text()).toBe('No public links') + expect(wrapper.find('oc-list-stub').exists()).toBeFalsy() }) }) describe('when linksLoading is set to true', () => { @@ -117,31 +120,18 @@ describe('FileLinks', () => { describe('when the add-new-link button is clicked', () => { let wrapper const spyAddNewLink = jest.spyOn(FileLinks.methods, 'addNewLink') - const spyLinkCreateTrigger = jest.spyOn(mapMutations, 'TRIGGER_PUBLIC_LINK_CREATE') beforeEach(() => { - const store = createStore() + const store = createStore({ links: [] }) wrapper = getMountedWrapper(store) }) it('should call addNewLink', async () => { expect(spyAddNewLink).toHaveBeenCalledTimes(0) - expect(spyLinkCreateTrigger).toHaveBeenCalledTimes(0) await wrapper.find(selectors.linkAddButton).trigger('click') expect(spyAddNewLink).toHaveBeenCalledTimes(1) - expect(spyLinkCreateTrigger).toHaveBeenCalledTimes(1) - }) - - it('should show link edit component', async () => { - expect(wrapper.find(linkEditStubSelector).exists()).toBeFalsy() - expect(wrapper.vm.currentView).toBe('showLinks') - - await wrapper.find(selectors.linkAddButton).trigger('click') - - expect(wrapper.vm.currentView).toBe('editPublicLink') - expect(wrapper.find(linkEditStubSelector).exists()).toBeTruthy() }) }) }) @@ -161,16 +151,6 @@ describe('FileLinks', () => { expect(wrapper.find(selectors.noResharePermissions).exists()).toBeTruthy() }) }) - - describe('when the private link functionality is disabled', () => { - it('private link should not be visible', () => { - const store = createStore() - store.getters.capabilities.files.privateLinks = false - const wrapper = getShallowWrapper(store) - - expect(wrapper.find(selectors.linkPrivate).exists()).toBeFalsy() - }) - }) }) function createStore({ @@ -185,9 +165,8 @@ describe('FileLinks', () => { expireDate = { enabled: true, days: 1, - enforced: '1' - }, - privateLinks = false + enforced: false + } } = {}) { return new Vuex.Store({ getters: { @@ -204,13 +183,17 @@ describe('FileLinks', () => { })), capabilities: jest.fn(() => { return { - files: { - privateLinks: privateLinks - }, files_sharing: { public: { defaultPublicLinkShareName: 'public link name default', - expire_date: expireDate + expire_date: expireDate, + password: { + enforced_for: { + read_only: false, + upload_only: false, + read_write: false + } + } } } } @@ -244,9 +227,6 @@ describe('FileLinks', () => { localVue, store: store, stubs: stubs, - provide: { - changeView: jest.fn() - }, mocks: { $route: { params: {} @@ -259,13 +239,7 @@ describe('FileLinks', () => { return mount(FileLinks, { localVue, store: store, - stubs: { - 'link-edit': true, - 'list-item': true - }, - provide: { - changeView: jest.fn() - }, + stubs: {}, mocks: { $route: { params: {} diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/CreateForm.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/CreateForm.spec.js new file mode 100644 index 00000000000..804443aa4c6 --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/CreateForm.spec.js @@ -0,0 +1,3 @@ +describe('CreateForm', () => { + it.todo('does not receive tests since this component will be deleted within few days') +}) diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/DetailsAndEdit.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/DetailsAndEdit.spec.js new file mode 100644 index 00000000000..13db93bad86 --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/DetailsAndEdit.spec.js @@ -0,0 +1,86 @@ +import { createLocalVue, shallowMount } from '@vue/test-utils' +import GetTextPlugin from 'vue-gettext' +import Vuex from 'vuex' +import DesignSystem from 'owncloud-design-system' +import stubs from '@/tests/unit/stubs' +import DetailsAndEdit from '@files/src/components/SideBar/Shares/Links/DetailsAndEdit.vue' +import { LinkShareRoles } from '@files/src/helpers/share' + +const localVue = createLocalVue() +localVue.use(DesignSystem) +localVue.use(Vuex) +localVue.use(GetTextPlugin, { + translations: 'does-not-matter.json', + silent: true +}) + +const availableRoleOptions = LinkShareRoles.list(false, true).map((role) => { + return { + role, + name: role.name, + label: role.label + } +}) + +const exampleLink = { + name: 'Example link', + url: 'https://some-url.com/abc', + permissions: '1' +} + +describe('DetailsAndEdit component', () => { + describe('if user can not edit', () => { + it('does not render dropdown or edit button', () => { + const wrapper = getShallowMountedWrapper(exampleLink) + expect(wrapper).toMatchSnapshot() + }) + }) + + describe('if user can edit', () => { + it('renders dropdown and edit button', () => { + const wrapper = getShallowMountedWrapper(exampleLink, false, true) + expect(wrapper).toMatchSnapshot() + }) + + it.todo('test edit options, button clicks and event handling/propagation') + }) +}) + +function getShallowMountedWrapper(link, expireDateEnforced = false, modifiable = false) { + return shallowMount(DetailsAndEdit, { + propsData: { + availableRoleOptions, + expirationDate: { + enforced: expireDateEnforced, + default: null, + min: 'Wed Apr 01 2020 00:00:00 GMT+0000 (Coordinated Universal Time)', + max: null + }, + link, + modifiable, + passwordEnforced: { + read_only: false, + upload_only: false, + read_write: false + } + }, + store: createStore(), + directives: { + 'oc-tooltip': jest.fn() + }, + stubs: { + ...stubs, + 'oc-datepicker': true + } + }) +} + +function createStore() { + return new Vuex.Store({ + actions: { + showMessage: jest.fn(), + createModal: jest.fn(), + hideModal: jest.fn() + } + }) +} diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/NameAndCopy.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/NameAndCopy.spec.js new file mode 100644 index 00000000000..5f0aa0b63bf --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/NameAndCopy.spec.js @@ -0,0 +1,36 @@ +import { createLocalVue, shallowMount } from '@vue/test-utils' +import GetTextPlugin from 'vue-gettext' +import DesignSystem from 'owncloud-design-system' +import NameAndCopy from '@files/src/components/SideBar/Shares/Links/NameAndCopy.vue' + +const localVue = createLocalVue() +localVue.use(DesignSystem) +localVue.use(GetTextPlugin, { + translations: 'does-not-matter.json', + silent: true +}) + +const exampleLink = { + name: 'Example link', + url: 'https://some-url.com/abc' +} + +describe('NameAndCopy', () => { + it('should show link info component', () => { + const wrapper = getShallowWrapper() + expect(wrapper).toMatchSnapshot() + }) +}) + +function getShallowWrapper() { + return shallowMount(NameAndCopy, { + localVue, + propsData: { + link: exampleLink + }, + stubs: { + 'oc-icon': true, + 'copy-to-clipboard-button': true + } + }) +} diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/DetailsAndEdit.spec.js.snap b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/DetailsAndEdit.spec.js.snap new file mode 100644 index 00000000000..ae1ce232e64 --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/DetailsAndEdit.spec.js.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DetailsAndEdit component if user can edit renders dropdown and edit button 1`] = ` + +`; + +exports[`DetailsAndEdit component if user can not edit does not render dropdown or edit button 1`] = ` + +`; diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/NameAndCopy.spec.js.snap b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/NameAndCopy.spec.js.snap new file mode 100644 index 00000000000..5f5df96b485 --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/__snapshots__/NameAndCopy.spec.js.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NameAndCopy should show link info component 1`] = ` +
+ + +
+`; diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkActions.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkActions.spec.js deleted file mode 100644 index 50725deffb1..00000000000 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkActions.spec.js +++ /dev/null @@ -1,147 +0,0 @@ -import LinkActions from '@files/src/components/SideBar/Shares/PublicLinks/LinkActions.vue' -import { createLocalVue, mount, shallowMount } from '@vue/test-utils' -import GetTextPlugin from 'vue-gettext' -import Vuex from 'vuex' -import DesignSystem from 'owncloud-design-system' - -const localVue = createLocalVue() -localVue.use(DesignSystem) -localVue.use(Vuex) -localVue.use(GetTextPlugin, { - translations: 'does-not-matter.json', - silent: true -}) - -const storeOptions = { - modules: { - Files: { - namespaced: true, - actions: { - removeLink: jest.fn() - }, - mutations: { - TRIGGER_PUBLIC_LINK_EDIT: jest.fn() - } - } - }, - actions: { - showMessage: jest.fn(), - createModal: jest.fn(), - hideModal: jest.fn() - } -} - -const selectors = { - editActionButton: '.oc-files-file-link-edit', - deleteActionButton: '.oc-files-file-link-delete' -} - -describe('LinkActions', () => { - describe('removal in progress', () => { - it('should render spinner if true', async () => { - const wrapper = getShallowMountedWrapper() - await wrapper.setData({ removalInProgress: true }) - const spinner = wrapper.find('oc-spinner-stub') - - expect(spinner.exists()).toBeTruthy() - expect(spinner.attributes('aria-label')).toBe('Removing public link') - - const actionLinks = wrapper.findAll('oc-button-stub') - expect(actionLinks.length).toBe(0) - }) - - it('should not render spinner if false', async () => { - const wrapper = getShallowMountedWrapper() - await wrapper.setData({ removalInProgress: false }) - const spinner = wrapper.find('oc-spinner-stub') - expect(spinner.exists()).toBeFalsy() - - const actionLinks = wrapper.findAll('oc-button-stub') - expect(actionLinks.length).toBe(2) - }) - }) - - describe('action buttons label', () => { - it('should set edit link button label', () => { - const wrapper = getShallowMountedWrapper() - const editLinkButton = wrapper.find(selectors.editActionButton) - - expect(editLinkButton.attributes('aria-label')).toBe('Edit public link') - }) - - it('should set remove link button label', () => { - const wrapper = getShallowMountedWrapper() - const editLinkButton = wrapper.find(selectors.deleteActionButton) - - expect(editLinkButton.attributes('aria-label')).toBe('Delete public link') - }) - }) - - describe('link buttons function calls', () => { - const editLinkSpy = jest.spyOn(LinkActions.methods, 'editLink') - const removeLinkSpy = jest.spyOn(LinkActions.methods, '$_removeLink') - const triggerPublicLinkEditSpy = jest.spyOn( - storeOptions.modules.Files.mutations, - 'TRIGGER_PUBLIC_LINK_EDIT' - ) - const wrapper = getMountedWrapper() - - it('should call "editLink" if edit link button is clicked', async () => { - const wrapper = getMountedWrapper() - const editButton = wrapper.find(selectors.editActionButton) - await editButton.trigger('click') - - expect(editLinkSpy).toHaveBeenCalledTimes(1) - expect(triggerPublicLinkEditSpy).toHaveBeenCalledTimes(1) - }) - - it('should call "$_removeLink" if delete link button is clicked', async () => { - const deleteButton = wrapper.find(selectors.deleteActionButton) - await deleteButton.trigger('click') - - expect(removeLinkSpy).toHaveBeenCalledTimes(1) - }) - }) -}) - -function getMountedWrapper() { - return mount(LinkActions, { - localVue, - propsData: { - link: {} - }, - provide: { - changeView: jest.fn() - }, - store: createStore(), - stubs: { - 'oc-button': false, - 'oc-icon': true, - 'oc-spinner': true - } - }) -} - -function getShallowMountedWrapper() { - return shallowMount(LinkActions, { - propsData: { - link: {} - }, - store: createStore(), - provide: { - changeView: jest.fn() - }, - directives: { - 'oc-tooltip': jest.fn() - }, - stubs: { - 'oc-button': true, - 'oc-icon': true, - 'oc-spinner': true - } - }) -} - -function createStore() { - return new Vuex.Store(storeOptions) -} diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkEdit.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkEdit.spec.js deleted file mode 100644 index 6fd1fef898c..00000000000 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkEdit.spec.js +++ /dev/null @@ -1,578 +0,0 @@ -import Vuex from 'vuex' -import { DateTime } from 'luxon' -import VueSelect from 'vue-select' -import GetTextPlugin from 'vue-gettext' -import stubs from '@/tests/unit/stubs/index.js' -import DesignSystem from 'owncloud-design-system' -import { createLocalVue, mount, shallowMount } from '@vue/test-utils' -import LinkEdit from '@files/src/components/SideBar/Shares/PublicLinks/LinkEdit.vue' -import { LinkShareRoles } from '../../../../../../src/helpers/share' - -const selectors = { - linkNameInput: '#oc-files-file-link-name', - linkErrorAlert: '.oc-files-file-link-error-alert', - linkExpireDatePicker: '#oc-files-file-link-expire-date', - linkExpireDateDeleteButton: '#oc-files-file-link-expire-date-delete', - linkPasswordField: '#oc-files-file-link-password', - linkPasswordDeleteButton: '#oc-files-file-link-password-delete', - linkCancelButton: '#oc-files-file-link-cancel', - linkCreateButton: '#oc-files-file-link-create', - linkSaveButton: '#oc-files-file-link-save', - linkSavingButton: '#oc-files-file-link-saving' -} - -const ocSpinnerStubSelector = 'oc-spinner-stub' - -const localVue = createLocalVue() -localVue.use(DesignSystem) -localVue.use(VueSelect) -localVue.use(Vuex) -localVue.use(GetTextPlugin, { - translations: 'does-not-matter.json', - silent: true -}) - -const mapActions = { - addLink: jest.fn(), - updateLink: jest.fn() -} - -describe('LinkEdit', () => { - beforeEach(() => { - jest.useFakeTimers('modern') - jest.setSystemTime(new Date(2020, 3, 1)) - }) - - afterEach(() => { - jest.useRealTimers() - }) - - describe('expiration date picker field', () => { - describe('required field label', () => {}) - describe('when the link expiration date is enforced', () => { - it('should have max-datetime attribute with tomorrow datetime value', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - publicLinkCapabilities: getLinkCapabilities({ - enforcedExpireDate: true, - days: 2 - }) - }) - ) - const expirationDatePickerFieldElement = wrapper.find(selectors.linkExpireDatePicker) - const expectedDate = new Date() - expectedDate.setDate(new Date().getDate() + 2) - - expect(expirationDatePickerFieldElement.attributes()['max-date']).toEqual( - expectedDate.toString() - ) - }) - }) - - it('should have min-datetime attribute with the value now', () => { - const wrapper = getShallowMountedWrapper() - const expirationDatePickerElement = wrapper.find('#oc-files-file-link-expire-date') - const expectedDate = new Date() - expect(expirationDatePickerElement.attributes()['min-date']).toEqual(expectedDate.toString()) - }) - - it('should be pre populated if the public link has already an expiration date set', () => { - const expectedDate = new Date() - expectedDate.setDate(expectedDate.getDate() + 1) - - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { - id: 1, - expireDate: expectedDate.toString() - }, - publicLinkCapabilities: getLinkCapabilities({ enabledExpireDate: true }) - }) - ) - const expirationDatePickerFieldElement = wrapper.find(selectors.linkExpireDatePicker) - - expect(expirationDatePickerFieldElement.attributes().value).toEqual(expectedDate.toString()) - }) - }) - - describe('expiration date delete button', () => { - it('should be present if the expiration date is not enforced and the link has expiration date set', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { - expireDate: DateTime.now().plus({ days: 1 }).toString() - } - }) - ) - const expirationDateDeleteButtonElement = wrapper.find(selectors.linkExpireDateDeleteButton) - - expect(expirationDateDeleteButtonElement.exists()).toBeTruthy() - }) - - it('should set expire date to empty string if clicked', async () => { - const expireDate = new Date() - expireDate.setDate(new Date().getDate() + 1) - - const wrapper = getMountedWrapper( - createStore({ - linkInEdit: { - expireDate - } - }) - ) - const expirationDateDeleteButtonElement = wrapper.find(selectors.linkExpireDateDeleteButton) - await expirationDateDeleteButtonElement.trigger('click') - - expect(wrapper.vm.expireDate).toEqual(null) - }) - - it('should not be present if expiration date is not enforced and the link in edit does not have an expiration date set', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { expireDate: null }, - publicLinkCapabilities: getLinkCapabilities({ - days: null - }) - }) - ) - const expirationDateDeleteButtonElement = wrapper.find(selectors.linkExpireDateDeleteButton) - - expect(expirationDateDeleteButtonElement.exists()).toBeFalsy() - }) - - it('should not be present if expiration date is enforced', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - publicLinkCapabilities: getLinkCapabilities({ enforcedExpireDate: true }) - }) - ) - const expirationDateDeleteButtonElement = wrapper.find(selectors.linkExpireDateDeleteButton) - - expect(expirationDateDeleteButtonElement.exists()).toBeFalsy() - }) - }) - - describe('role select field', () => { - it.each(['folder', 'file'])( - 'should list all available link sharing roles according to resource type %s', - (type) => { - const wrapper = getMountedWrapper(createStore({ type })) - const roleSelectElement = wrapper.findComponent(VueSelect) - const actualOptions = roleSelectElement.props().options - - const linkShareRoles = LinkShareRoles.list(type === 'folder') - expect(actualOptions.length).toBe(linkShareRoles.length) - for (let i = 0; i < linkShareRoles.length; i++) { - expect(actualOptions[i].name).toBe(linkShareRoles[i].name) - } - } - ) - - it('should not be clearable', () => { - const wrapper = getMountedWrapper() - const roleSelectElement = wrapper.findComponent(VueSelect) - - expect(roleSelectElement.props().clearable).toBeFalsy() - }) - - it('should set selected role when input is triggered', () => { - const wrapper = getMountedWrapper(createStore({ type: 'folder' })) - const roleSelectElement = wrapper.findComponent(VueSelect) - - const option = roleSelectElement.vm.options[2] - roleSelectElement.vm.select(option) - expect(wrapper.vm.selectedRole.name).toBe(option.name) - }) - }) - - describe('password field', () => { - it('should have required label if password is enforced', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - publicLinkCapabilities: getLinkCapabilities({ - passwordEnforcedFor: { - read_only: '1' - } - }) - }) - ) - const passwordFieldElement = wrapper.find(selectors.linkPasswordField) - - expect(passwordFieldElement).toMatchSnapshot() - }) - - it('should not have required label if password is not enforced', () => { - const wrapper = getShallowMountedWrapper() - const passwordFieldElement = wrapper.find(selectors.linkPasswordField) - - expect(passwordFieldElement).toMatchSnapshot() - }) - }) - - describe('link password delete button', () => { - it('should be present if password is not enforced and the link has a password set', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { - hasPassword: true - } - }) - ) - const passwordDeleteButtonElement = wrapper.find(selectors.linkPasswordDeleteButton) - - expect(passwordDeleteButtonElement.exists()).toBeTruthy() - }) - - it('should not be present if password is not enforced', () => { - const wrapper = getShallowMountedWrapper() - const passwordDeleteButtonElement = wrapper.find(selectors.linkPasswordDeleteButton) - - expect(passwordDeleteButtonElement.exists()).toBeFalsy() - }) - it('should not be present if the link does not has a password set', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { - hasPassword: false - } - }) - ) - const passwordDeleteButtonElement = wrapper.find(selectors.linkPasswordDeleteButton) - - expect(passwordDeleteButtonElement.exists()).toBeFalsy() - }) - - it('should be present if password is not enforced and password value is set', async () => { - const wrapper = getMountedWrapper( - createStore({ - linkInEdit: {} - }) - ) - const linkPasswordInputElement = wrapper.find(selectors.linkPasswordField) - await linkPasswordInputElement.setValue('VeryStrongPassword') - - const passwordDeleteButtonElement = wrapper.find(selectors.linkPasswordDeleteButton) - - expect(passwordDeleteButtonElement.exists()).toBeTruthy() - }) - - it('should remove password if clicked', async () => { - const wrapper = getMountedWrapper( - createStore({ - linkInEdit: {} - }) - ) - const linkPasswordInputElement = wrapper.find(selectors.linkPasswordField) - await linkPasswordInputElement.setValue('VeryStrongPassword') - - const passwordDeleteButtonElement = wrapper.find(selectors.linkPasswordDeleteButton) - await passwordDeleteButtonElement.trigger('click') - - expect(wrapper.vm.password).toBe('') - expect(wrapper.vm.hasPassword).toBeFalsy() - }) - }) - - describe('link edit form validation', () => { - it('should disable the link save button if the expiration date value is not valid', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { - id: 1224, - name: 'Public Link', - hasPassword: true, - // for an enforced expireDate, empty expireDate is an invalid value - expireDate: '' - }, - publicLinkCapabilities: getLinkCapabilities({ enforcedExpireDate: true }) - }), - { - saving: false - } - ) - - expect(wrapper.find(selectors.linkSaveButton).attributes('disabled')).toBe('true') - }) - - it('should disable the link save button if the password field is invalid', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { id: 1224, name: 'Public Link' }, - publicLinkCapabilities: getLinkCapabilities({ - passwordEnforcedFor: { - read_only: '1' - } - }) - }), - { saving: false } - ) - - expect(wrapper.find(selectors.linkSaveButton).attributes('disabled')).toBe('true') - }) - - it('should enable link save button if the expiration and password fields are valid', () => { - const expireDate = new Date() - expireDate.setDate(new Date().getDate() + 1) - - const wrapper = getMountedWrapper( - createStore({ - linkInEdit: { - id: 1224, - name: 'Public Link', - hasPassword: true, - expireDate - }, - publicLinkCapabilities: getLinkCapabilities({ enforcedExpireDate: true }) - }), - { - saving: false - } - ) - - expect(wrapper.find(selectors.linkSaveButton).attributes('disabled')).toBeUndefined() - }) - }) - - describe('grid', () => { - it('should disable the cancel link button if saving is set to true', () => { - const wrapper = getShallowMountedWrapper(createStore(), { saving: true }) - - const cancelLinkButtonElement = wrapper.find(selectors.linkCancelButton) - - expect(cancelLinkButtonElement.attributes('disabled')).toBe('true') - }) - describe('saving button', () => { - describe('when saving is set to true', () => { - it('should show the text "Creating" if a new link is being created', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { name: 'Public Link' } - }), - { saving: true } - ) - const savingButtonElement = wrapper.find(selectors.linkSavingButton) - - expect(savingButtonElement.exists()).toBeTruthy() - expect(savingButtonElement.text()).toBe('Creating') - expect(savingButtonElement.find('oc-spinner-stub').attributes('arialabel')).toBe( - 'Creating Public Link' - ) - }) - it('should show the text "Saving" during the update process', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { id: 1223, name: 'Public Link' } - }), - { saving: true } - ) - const savingButtonElement = wrapper.find(selectors.linkSavingButton) - - expect(savingButtonElement.exists()).toBeTruthy() - expect(savingButtonElement.text()).toBe('Saving') - expect(savingButtonElement.find(ocSpinnerStubSelector).attributes('arialabel')).toBe( - 'Saving Public Link' - ) - }) - }) - describe('when saving is set to false', () => { - describe('when new link is being created', () => { - it('should show the link create button', () => { - const wrapper = getShallowMountedWrapper(createStore(), { saving: false }) - const linkCreateButton = wrapper.find(selectors.linkCreateButton) - expect(linkCreateButton.exists()).toBeTruthy() - expect(linkCreateButton.attributes('disabled')).toBeFalsy() - }) - it('should trigger "addLink" function if clicked', async () => { - const addLinkSpy = jest.spyOn(mapActions, 'addLink') - const wrapper = getMountedWrapper( - createStore({ - linkInEdit: { name: 'Public Link', hasPassword: true }, - publicLinkCapabilities: getLinkCapabilities({ enforcedExpireDate: true }) - }), - { saving: false } - ) - const linkCreateButton = wrapper.find(selectors.linkCreateButton) - expect(linkCreateButton.attributes('disabled')).toBeFalsy() - - await linkCreateButton.trigger('click') - expect(wrapper.vm.saving).toBeTruthy() - expect(addLinkSpy).toHaveBeenCalledTimes(1) - }) - }) - describe('when existing link is being updated', () => { - it('should show the link save button', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { id: 1224, name: 'Public Link' } - }), - { saving: false } - ) - - const linkSaveButton = wrapper.find(selectors.linkSaveButton) - expect(linkSaveButton.exists()).toBeTruthy() - }) - it('should set the link save button as disabled if form is not valid', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { id: 1224, name: 'Public Link' }, - // invalid password field - // by default viewer role is selected with permission 1 (viewer) - // combined with enforced for password is enforce for link form - // since link password is enforced and password is not set, form will be invalid - publicLinkCapabilities: getLinkCapabilities({ - passwordEnforcedFor: { - read_only: '1' - } - }) - }), - { saving: false } - ) - const linkSaveButton = wrapper.find(selectors.linkSaveButton) - - expect(linkSaveButton.attributes('disabled')).toBeTruthy() - }) - it('should set the link save button as disabled if the form does not have any changes', () => { - const wrapper = getShallowMountedWrapper( - createStore({ - linkInEdit: { id: 1224, name: 'Public Link', hasPassword: false, permissions: 1 }, - publicLinkCapabilities: getLinkCapabilities({ enforcedExpireDate: true }) - }), - { saving: false } - ) - const linkSaveButton = wrapper.find(selectors.linkSaveButton) - - expect(linkSaveButton.attributes('disabled')).toBe('true') - }) - - describe('when the form is valid and has some changes', () => { - const updateLinkSpy = jest.spyOn(mapActions, 'updateLink') - - it('should trigger "updateLink" method if clicked', async () => { - const wrapper = getMountedWrapper( - createStore({ - linkInEdit: { id: 1224, name: 'Public Link', hasPassword: true } - }), - { - saving: false - } - ) - - // make some changes in the form - const nameInput = wrapper.find(selectors.linkNameInput) - await nameInput.setValue('Link changed') - const linkSaveButton = wrapper.find(selectors.linkSaveButton) - - expect(linkSaveButton.attributes('disabled')).toBeUndefined() - expect(wrapper.vm.saving).toBeFalsy() - expect(updateLinkSpy).not.toHaveBeenCalled() - - await linkSaveButton.trigger('click') - - expect(wrapper.vm.saving).toBeTruthy() - expect(linkSaveButton.attributes('disabled')).toBe('disabled') - expect(updateLinkSpy).toHaveBeenCalledTimes(1) - }) - }) - }) - }) - }) - }) -}) - -function createStore({ - linkInEdit = {}, - publicLinkCapabilities = getLinkCapabilities(), - type = 'files' -} = {}) { - return new Vuex.Store({ - modules: { - Files: { - namespaced: true, - state: { - publicLinkInEdit: linkInEdit - }, - getters: { - highlightedFile: function () { - return { type: type, isFolder: type === 'folder' } - } - }, - actions: mapActions - } - }, - getters: { - getToken: jest.fn(), - capabilities: function () { - return { - files_sharing: { - public: publicLinkCapabilities - } - } - } - } - }) -} - -function getLinkCapabilities({ - enforcedExpireDate = false, - days = 1, - enabledExpireDate = false, - passwordEnforcedFor = false -} = {}) { - return { - expire_date: { - enabled: enabledExpireDate, - days: days, - enforced: enforcedExpireDate - }, - password: { - enforced_for: passwordEnforcedFor - } - } -} - -function getShallowMountedWrapper(store = createStore(), data = {}) { - const wrapper = shallowMount(LinkEdit, { - ...mountOptions(data, store), - stubs: { - ...stubs, - 'oc-text-input': true, - 'oc-select': true, - 'oc-datepicker': true, - 'role-item': true, - 'oc-icon': true, - 'oc-progress': true - } - }) - wrapper.vm.$refs.nameInput.focus = jest.fn() - return wrapper -} - -function getMountedWrapper(store = createStore(), data = {}) { - return mount(LinkEdit, { - ...mountOptions(data, store), - stubs: { - 'vue-select': VueSelect - }, - mocks: { - $route: { - params: { - storageId: 1 - } - } - } - }) -} - -const mountOptions = (data, store) => ({ - localVue, - store, - provide: { - changeView: jest.fn() - }, - directives: { - translate: jest.fn() - }, - data() { - return data - } -}) diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkInfo.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkInfo.spec.js deleted file mode 100644 index 98296b241f5..00000000000 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/LinkInfo.spec.js +++ /dev/null @@ -1,235 +0,0 @@ -import { DateTime } from 'luxon' -import stubs from '@/tests/unit/stubs' -import GetTextPlugin from 'vue-gettext' -import DesignSystem from 'owncloud-design-system' -import { mount, shallowMount, createLocalVue } from '@vue/test-utils' -import LinkInfo from '@files/src/components/SideBar/Shares/PublicLinks/LinkInfo.vue' - -const selectors = { - linkName: '.oc-files-file-link-name', - linkExpirationInfo: '.oc-files-public-link-expires', - linkPassword: '.oc-files-file-link-password', - linkIndirect: '.oc-files-file-link-via', - linkViaLabel: '.files-file-links-link-via-label', - linkRole: '.oc-files-file-link-role', - linkCopyUrl: '.oc-files-public-link-copy-url', - linkUrl: '.oc-files-file-link-url' -} -const localVue = createLocalVue() -localVue.use(GetTextPlugin, { - translations: 'does-not-matter.json', - silent: true -}) - -describe('LinkInfo', () => { - describe('link name', () => { - it('should show token as link name if link does not have name', () => { - const wrapper = getShallowWrapper() - - expect(wrapper.vm.linkName).toEqual('122235445488') - expect(wrapper.find(selectors.linkName).text()).toEqual('122235445488') - }) - - it('should show link name if provided link has name attribute', () => { - const wrapper = getShallowWrapper({ - name: 'some-name', - url: 'some-link' - }) - - expect(wrapper.vm.linkName).toEqual('some-name') - expect(wrapper.find(selectors.linkName).text()).toEqual('some-name') - }) - - it('should show link name if provided link has both name and token attribute', () => { - const wrapper = getShallowWrapper({ - name: 'some-name', - token: 'some-token', - url: 'some-link' - }) - - expect(wrapper.vm.linkName).toEqual('some-name') - expect(wrapper.find(selectors.linkName).text()).toEqual('some-name') - }) - }) - - describe('link url', () => { - it('should set provided link url as href attribute of link tag and as value of copy to clipboard button', () => { - const wrapper = getShallowWrapper({ - url: 'some-link' - }) - const linkCopyUrlHyperlink = wrapper.find(selectors.linkUrl) - - expect(wrapper.vm.link.url).toEqual('some-link') - expect(linkCopyUrlHyperlink.attributes().href).toEqual('some-link') - expect(linkCopyUrlHyperlink.text()).toEqual('some-link') - }) - }) - - describe('link role', () => { - it.each([ - { role: 'Viewer', icon: 'eye' }, - { role: 'Editor', icon: 'pencil' }, - { role: 'Contributor', icon: 'pencil' }, - { role: 'Uploader', icon: 'upload' }, - { role: '*', icon: 'key' } - ])('should set different role tag icon for different role types', (dataSet) => { - const wrapper = getShallowWrapper({ - url: 'some-link', - description: dataSet.role - }) - - expect(wrapper.find(`${selectors.linkRole} oc-icon-stub`).attributes().name).toEqual( - dataSet.icon - ) - expect(wrapper.find(selectors.linkRole).text()).toEqual(dataSet.role) - }) - }) - describe('copy to clipboard button', () => { - const wrapper = getShallowWrapper({ - url: 'some-link' - }) - const linkCopyUrlButton = wrapper.find(selectors.linkCopyUrl) - - it('should set link url as button value', () => { - expect(linkCopyUrlButton.attributes().value).toEqual('some-link') - }) - - it('should have label set', () => { - expect(linkCopyUrlButton.props().label).toBe('Copy link to clipboard') - }) - - it('should have success message title set', () => { - expect(linkCopyUrlButton.props().successMsgTitle).toBe('Public link copied') - }) - - it('should have success message text set', () => { - const wrapper = getShallowWrapper({ - url: 'some-link', - name: 'some name' - }) - const linkCopyUrlButton = wrapper.find(selectors.linkCopyUrl) - - expect(linkCopyUrlButton.props().successMsgText).toBe( - 'The public link "some name" has been copied to your clipboard.' - ) - }) - }) - - describe('link expiration', () => { - it('should exist if link has expiration', () => { - const tomorrow = DateTime.now().plus({ days: 1 }) - const wrapper = getShallowWrapper({ - url: 'some-link', - expiration: tomorrow - }) - - const linkExpiration = wrapper.find(selectors.linkExpirationInfo) - - expect(linkExpiration.exists()).toBeTruthy() - expect(linkExpiration.find('translate-stub').props().translateParams).toMatchObject({ - expires: 'in 1 day' - }) - }) - it('should not be present if link does not have expiration', () => { - const wrapper = getShallowWrapper({ - url: 'some-link' - }) - - expect(wrapper.find(selectors.linkExpirationInfo).exists()).toBeFalsy() - }) - }) - describe('link password', () => { - it('should exist if link has password', () => { - const wrapper = getShallowWrapper({ - url: 'some-link', - password: 'some-password' - }) - - expect(wrapper.find(selectors.linkPassword).exists()).toBeTruthy() - }) - it('should not be present if link does not have password', () => { - const wrapper = getShallowWrapper({ - url: 'some-link' - }) - - expect(wrapper.find(selectors.linkPassword).exists()).toBeFalsy() - }) - }) - describe('indirect link', () => { - it('should exist if link is indirect', () => { - const wrapper = getMountedWrapper({ - url: 'some-link', - indirect: true, - path: '/some-path' - }) - const linkDirectTag = wrapper.find(selectors.linkIndirect) - - expect(linkDirectTag.exists()).toBeTruthy() - expect(linkDirectTag.props().to).toMatchObject({ - name: 'files-spaces-personal-home', - params: { item: '/some-path' }, - query: { scrollTo: 'some-path' } - }) - expect(linkDirectTag.find(selectors.linkViaLabel).text()).toBe('Via some-path') - }) - it('should not exist if link is not indirect', () => { - const wrapper = getShallowWrapper({ - url: 'some-link' - }) - - expect(wrapper.find(selectors.linkIndirect).exists()).toBeFalsy() - }) - }) -}) - -function getShallowWrapper( - link = { - token: '122235445488', - url: 'some-link' - } -) { - return shallowMount(LinkInfo, { - localVue, - stubs: { ...stubs, 'oc-tag': true }, - propsData: { - link: link - }, - directives: { - 'oc-tooltip': jest.fn() - } - }) -} - -function getMountedWrapper( - link = { - token: '122235445488', - url: 'some-link' - } -) { - localVue.use(DesignSystem) - return mount(LinkInfo, { - localVue, - propsData: { - link: link - }, - stubs, - directives: { - 'oc-tooltip': jest.fn() - }, - mocks: { - $router: { - currentRoute: { name: 'some-route' }, - resolve: (r) => { - return { - href: r.name - } - } - }, - $route: { - params: { - storageId: 1 - } - } - } - }) -} diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/ListItem.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/ListItem.spec.js deleted file mode 100644 index 12b3636f3bb..00000000000 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/ListItem.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -import ListItem from '@files/src/components/SideBar/Shares/PublicLinks/ListItem.vue' -import { createLocalVue, shallowMount } from '@vue/test-utils' - -const localVue = createLocalVue() - -describe('ListItem', () => { - it('should show link info component', () => { - const wrapper = getShallowWrapper(getLinkObject()) - expect(wrapper.find('link-info-stub').props('link')).toMatchObject({ - name: 'public link', - url: 'some-url', - indirect: false - }) - }) - - it('should show link actions component if link is not indirect', () => { - const wrapper = getShallowWrapper(getLinkObject()) - const linkActions = wrapper.find('link-actions-stub') - expect(linkActions.exists()).toBeTruthy() - expect(linkActions.props('link')).toMatchObject({ - name: 'public link', - url: 'some-url', - indirect: false - }) - }) - it('should not show link actions component if link is indirect', () => { - const wrapper = getShallowWrapper(getLinkObject(true)) - const linkActions = wrapper.find('link-actions-stub') - expect(linkActions.exists()).toBeFalsy() - }) -}) - -function getLinkObject(indirect = false) { - return { - link: { - name: 'public link', - url: 'some-url', - indirect: indirect - } - } -} - -function getShallowWrapper(props) { - return shallowMount(ListItem, { - localVue, - propsData: props, - stubs: { - 'oc-grid': true, - 'link-info': true, - 'link-actions': true - } - }) -} diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/__snapshots__/LinkEdit.spec.js.snap b/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/__snapshots__/LinkEdit.spec.js.snap deleted file mode 100644 index 552659bf3a5..00000000000 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/PublicLinks/__snapshots__/LinkEdit.spec.js.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LinkEdit password field should have required label if password is enforced 1`] = ``; - -exports[`LinkEdit password field should not have required label if password is not enforced 1`] = ``; diff --git a/packages/web-runtime/package.json b/packages/web-runtime/package.json index 4252ad07ba0..8f596afb104 100644 --- a/packages/web-runtime/package.json +++ b/packages/web-runtime/package.json @@ -56,7 +56,7 @@ "xml-js": "^1.6.11" }, "devDependencies": { - "@types/luxon": "^2.0.8", + "@types/luxon": "^2.3.1", "@types/semver": "^7.3.8" } } diff --git a/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md b/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md index ecdf7b485e3..b2dd21161e9 100644 --- a/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md +++ b/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md @@ -171,10 +171,10 @@ Other free text and markdown formatting can be used elsewhere in the document if - [webUISharingPublicBasic/publicLinkEdit.feature:31](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L31) - [webUISharingPublicBasic/publicLinkEdit.feature:32](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L32) - [webUISharingPublicBasic/publicLinkEdit.feature:33](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L33) -- [webUISharingPublicBasic/publicLinkEdit.feature:56](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L56) - [webUISharingPublicBasic/publicLinkEdit.feature:57](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L57) - [webUISharingPublicBasic/publicLinkEdit.feature:58](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L58) - [webUISharingPublicBasic/publicLinkEdit.feature:59](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L59) +- [webUISharingPublicBasic/publicLinkEdit.feature:60](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature#L60) - [webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature:51](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature#L51) - [webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature:52](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature#L52) - [webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature:71](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature#L71) diff --git a/tests/acceptance/features/webUISharingPublicBasic/publicLinkCreate.feature b/tests/acceptance/features/webUISharingPublicBasic/publicLinkCreate.feature index ff84d69322d..9e00619845f 100644 --- a/tests/acceptance/features/webUISharingPublicBasic/publicLinkCreate.feature +++ b/tests/acceptance/features/webUISharingPublicBasic/publicLinkCreate.feature @@ -14,12 +14,12 @@ Feature: Create public link shares And user "Alice" has logged in using the webUI When the user creates a new public link for resource "simple-folder" using the webUI Then user "Alice" should have a share with these details in the server: - | field | value | - | share_type | public_link | - | uid_owner | Alice | - | permissions | read | - | path | /simple-folder | - | name | Public link | + | field | value | + | share_type | public_link | + | uid_owner | Alice | + | permissions | read | + | path | /simple-folder | + | name | Public link | And a link named "Public link" should be listed with role "Viewer" in the public link list of resource "simple-folder" on the webUI When the public uses the webUI to access the last public link created by user "Alice" in a new session Then file "lorem.txt" should be listed on the webUI @@ -30,12 +30,12 @@ Feature: Create public link shares And user "Alice" has logged in using the webUI When the user creates a new public link for resource "lorem.txt" using the webUI Then user "Alice" should have a share with these details in the server: - | field | value | - | share_type | public_link | - | uid_owner | Alice | - | permissions | read | - | path | /lorem.txt | - | name | Public link | + | field | value | + | share_type | public_link | + | uid_owner | Alice | + | permissions | read | + | path | /lorem.txt | + | name | Public link | And a link named "Public link" should be listed with role "Viewer" in the public link list of resource "lorem.txt" on the webUI When the public uses the webUI to access the last public link created by user "Alice" in a new session Then file "lorem.txt" should be listed on the webUI @@ -48,11 +48,11 @@ Feature: Create public link shares And user "Alice" has logged in using the webUI When the user creates a new public link for resource "simple-folder" using the webUI Then user "Alice" should have a share with these details in the server: - | field | value | - | share_type | public_link | - | uid_owner | Alice | - | permissions | read | - | path | /simple-folder | + | field | value | + | share_type | public_link | + | uid_owner | Alice | + | permissions | read | + | path | /simple-folder | And a public link with the last created link share token as name should be listed for resource "simple-folder" on the webUI When the public uses the webUI to access the last public link created by user "Alice" in a new session Then file "lorem.txt" should be listed on the webUI @@ -64,11 +64,11 @@ Feature: Create public link shares And user "Alice" has logged in using the webUI When the user creates a new public link for resource "lorem.txt" using the webUI Then user "Alice" should have a share with these details in the server: - | field | value | - | share_type | public_link | - | uid_owner | Alice | - | permissions | read | - | path | /lorem.txt | + | field | value | + | share_type | public_link | + | uid_owner | Alice | + | permissions | read | + | path | /lorem.txt | And a public link with the last created link share token as name should be listed for resource "lorem.txt" on the webUI When the public uses the webUI to access the last public link created by user "Alice" in a new session Then file "lorem.txt" should be listed on the webUI @@ -93,14 +93,14 @@ Feature: Create public link shares When the user creates a new public link for file "lorem.txt" using the webUI And the user opens folder "simple-folder" using the webUI And the user creates a new public link for file "lorem.txt" using the webUI - And the user browses to the shared-via-link page using the webUI + # using the webui to navigate creates a problem because "successfully created link" notifications block the nav + And the user has browsed to the shared-via-link page Then file with path "lorem.txt" should be listed on the webUI And file with path "simple-folder/lorem.txt" should be listed on the webUI - Scenario: user creates a multiple public link of a file and delete the first link Given user "Alice" has created file "lorem.txt" in the server And user "Alice" has created a public link with following settings in the server diff --git a/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature b/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature index c29af9ca34a..a612d2d3426 100644 --- a/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature +++ b/tests/acceptance/features/webUISharingPublicBasic/publicLinkEdit.feature @@ -7,6 +7,7 @@ Feature: Edit public link shares Background: Given user "Alice" has been created with default attributes and without skeleton files in the server + @issue-ocis-1328 Scenario Outline: user tries to change the role of an existing public link role without entering share password while enforce password for that role is enforced Given the setting "" of app "core" has been set to "yes" in the server @@ -16,9 +17,8 @@ Feature: Edit public link shares | name | Public-link | | permissions | | And user "Alice" has logged in using the webUI - When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing following - | role | | - Then the user should see an error message on the public link share dialog saying "Passwords are enforced for link shares" + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the role to "" + Then the user should see an error message on the public link share dialog saying "Role requires password to be set for link" And user "Alice" should have a share with these details in the server: | field | value | | share_type | public_link | @@ -32,6 +32,8 @@ Feature: Edit public link shares | read | Editor | shareapi_enforce_links_password_read_write_delete | | read, create | Uploader | shareapi_enforce_links_password_write_only | + + @issue-ocis-1328 Scenario Outline: user tries to delete the password of an existing public link role while enforce password for that role is enforced Given the setting "" of app "core" has been set to "yes" in the server @@ -42,9 +44,8 @@ Feature: Edit public link shares | permissions | | | password | 123 | And user "Alice" has logged in using the webUI - When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing following - | password | | - Then the user should see an error message on the public link share dialog saying "Passwords are enforced for link shares" + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the password to "" + Then the user should see an error message on the public link edit modal dialog saying "Password can't be empty" And user "Alice" should have a share with these details in the server: | field | value | | share_type | public_link | @@ -58,45 +59,6 @@ Feature: Edit public link shares | read, update, create, delete | shareapi_enforce_links_password_read_write_delete | | create | shareapi_enforce_links_password_write_only | - @issue-ocis-1328 - Scenario Outline: user changes the role of an existing public link role without entering share password while enforce password for the original role is enforced - Given the setting "" of app "core" has been set to "yes" in the server - And user "Alice" has created folder "simple-folder" in the server - And user "Alice" has created a public link with following settings in the server - | path | simple-folder | - | name | Public-link | - | permissions | | - | password | 123 | - And user "Alice" has logged in using the webUI - When the user edits the public link named "Public-link" of folder "simple-folder" changing following - | role | | - | password | | - Then user "Alice" should have a share with these details in the server: - | field | value | - | share_type | public_link | - | uid_owner | Alice | - | permissions | | - | path | /simple-folder | - Examples: - | initial-permissions | role | setting-name | expected-permissions | - | read | Contributor | shareapi_enforce_links_password_read_only | read, create | - | read, create | Viewer | shareapi_enforce_links_password_read_write | read | - | read, update, create, delete | Uploader | shareapi_enforce_links_password_read_write_delete | create | - | create | Editor | shareapi_enforce_links_password_write_only | read, update, create, delete | - - @issue-ocis-1328 - Scenario: user edits a public link and does not save the changes - Given the setting "shareapi_allow_public_notification" of app "core" has been set to "yes" in the server - And user "Alice" has created folder "simple-folder" in the server - And user "Alice" has created a public link with following settings in the server - | path | simple-folder | - | name | test_public_link | - | password | pass123 | - And user "Alice" has logged in using the webUI - When the user edits the public link named "test_public_link" of folder "simple-folder" changing following but not saving - | password | qwertyui | - And the public uses the webUI to access the last public link created by user "Alice" with password "qwertyui" in a new session - Then the public should not get access to the publicly shared file Scenario: user edits a name of an already existing public link Given user "Alice" has created folder "simple-folder" in the server @@ -107,11 +69,11 @@ Feature: Edit public link shares | name | Public-link | | permissions | read | | password | pass123 | - When the user edits the public link named "Public-link" of folder "simple-folder" changing following - | name | simple-folder Share | + When the user renames the public link named "Public-link" of folder "simple-folder" to "simple-folder Share" And the public uses the webUI to access the last public link created by user "Alice" with password "pass123" in a new session Then file "lorem.txt" should be listed on the webUI + Scenario: user edits the password of an already existing public link Given user "Alice" has created folder "simple-folder" in the server And user "Alice" has created file "simple-folder/lorem.txt" in the server @@ -121,8 +83,7 @@ Feature: Edit public link shares | permissions | read, update, create, delete | | password | pass123 | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public-link" of folder "simple-folder" changing following - | password | qwertyui | + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the password to "qwertyui" And the public uses the webUI to access the last public link created by user "Alice" with password "qwertyui" in a new session Then file "lorem.txt" should be listed on the webUI @@ -136,8 +97,7 @@ Feature: Edit public link shares | permissions | read, update, create, delete | | password | pass123 | And user "Alice" has logged in using the webUI - When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing following - | password | qwertyui | + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the password to "qwertyui" And the public uses the webUI to access the last public link created by user "Alice" with password "pass123" in a new session Then the public should not get access to the publicly shared file @@ -150,8 +110,7 @@ Feature: Edit public link shares | name | Public-link | | permissions | read, update, create, delete | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public-link" of folder "simple-folder" changing following - | role | Viewer | + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the role to "Viewer" And the public uses the webUI to access the last public link created by user "Alice" in a new session Then file "lorem.txt" should be listed on the webUI And it should not be possible to delete file "lorem.txt" using the webUI @@ -166,8 +125,7 @@ Feature: Edit public link shares | name | Public-link | | permissions | read | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public-link" of folder "simple-folder" changing following - | role | Editor | + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the role to "Editor" And the public uses the webUI to access the last public link created by user "Alice" in a new session And the user deletes the following elements using the webUI | name | @@ -196,8 +154,7 @@ Feature: Edit public link shares | name | Public-link | | permissions | read, update, create, delete | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public-link" of folder "simple-folder" changing following - | role | Contributor | + When the user tries to edit the public link named "Public-link" of folder "simple-folder" changing the role to "Contributor" And the public uses the webUI to access the last public link created by user "Alice" in a new session And the user uploads file "lorem.txt" using the webUI Then file "simple.txt" should be listed on the webUI @@ -207,10 +164,9 @@ Feature: Edit public link shares Scenario: assign password to already created public share Given user "Alice" has created file "lorem.txt" in the server And user "Alice" has created a public link with following settings in the server - | path | lorem.txt | - | name | Public-link | + | path | lorem.txt | + | name | Public-link | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public-link" of file "lorem.txt" changing following - | password | pass123 | + When the user tries to edit the public link named "Public-link" of folder "lorem.txt" adding a password "pass123" And the public uses the webUI to access the last public link created by user "Alice" with password "pass123" in a new session Then file "lorem.txt" should be listed on the webUI diff --git a/tests/acceptance/features/webUISharingPublicDifferentRoles/shareByPublicLinkDifferentRoles.feature b/tests/acceptance/features/webUISharingPublicDifferentRoles/shareByPublicLinkDifferentRoles.feature index 94db04ff24d..acf0005e7a2 100644 --- a/tests/acceptance/features/webUISharingPublicDifferentRoles/shareByPublicLinkDifferentRoles.feature +++ b/tests/acceptance/features/webUISharingPublicDifferentRoles/shareByPublicLinkDifferentRoles.feature @@ -279,36 +279,36 @@ Feature: Share by public link with different roles Given the setting "shareapi_enforce_links_password_read_only" of app "core" has been set to "yes" in the server And user "Alice" has logged in using the webUI When the user creates a new public link for folder "simple-folder" using the webUI - Then the user should see an error message on the public link share dialog saying "Passwords are enforced for link shares" And user "Alice" should not have created any shares in the server + @issue-ocis-1328 Scenario: user tries to create a public link with Contributor role without entering share password while enforce password on read-write public share is enforced Given the setting "shareapi_enforce_links_password_read_write" of app "core" has been set to "yes" in the server And user "Alice" has logged in using the webUI When the user creates a new public link for folder "simple-folder" using the webUI with | role | Contributor | - Then the user should see an error message on the public link share dialog saying "Passwords are enforced for link shares" And user "Alice" should not have created any shares in the server + @issue-ocis-1328 Scenario: user tries to create a public link with Editor Role without entering share password while enforce password on read-write public share is enforced Given the setting "shareapi_enforce_links_password_read_write_delete" of app "core" has been set to "yes" in the server And user "Alice" has logged in using the webUI When the user creates a new public link for folder "simple-folder" using the webUI with | role | Editor | - Then the user should see an error message on the public link share dialog saying "Passwords are enforced for link shares" And user "Alice" should not have created any shares in the server + @issue-ocis-1328 Scenario: user tries to create a public link with Uploader role without entering share password while enforce password on write only public share is enforced Given the setting "shareapi_enforce_links_password_write_only" of app "core" has been set to "yes" in the server And user "Alice" has logged in using the webUI When the user creates a new public link for folder "simple-folder" using the webUI with | role | Uploader | - Then the user should see an error message on the public link share dialog saying "Passwords are enforced for link shares" And user "Alice" should not have created any shares in the server + @issue-ocis-1328 Scenario: user creates a public link with Contributor Role without entering share password while enforce password on read only public share is enforced Given the setting "shareapi_enforce_links_password_read_only" of app "core" has been set to "yes" in the server diff --git a/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature b/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature index de1fc116bc2..c8c6afa39b4 100644 --- a/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature +++ b/tests/acceptance/features/webUISharingPublicExpire/shareByPublicLinkExpiringLinks.feature @@ -15,8 +15,7 @@ Feature: Share by public link | name | Public link | | expireDate | 2038-10-14 | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public link" of file "lorem.txt" changing following - | expireDate | 2038 July 21 | + When the user edits the public link named "Public link" of file "lorem.txt" changing expireDate to "2038 July 21" Then the last public link share response of user "Alice" should include the following fields in the server | expireDate | 2038-07-21 | @@ -32,6 +31,7 @@ Feature: Share by public link + @issue-ocis-1328 Scenario Outline: auto set expiration date on public link (with default amount of expiry days) Given the setting "shareapi_default_expire_date" of app "core" has been set to "yes" in the server @@ -114,9 +114,7 @@ Feature: Share by public link | expireDate | +16 | And user "Alice" has logged in using the webUI And the setting "shareapi_expire_after_n_days" of app "core" has been set to "7" in the server - When the user edits the public link named "Public link" of file "lorem.txt" changing following - | expireDate | +15 | - Then the user should see an error message on the public link share dialog saying "Cannot set expiration date more than 7 days in the future" + When the user edits the public link named "Public link" of file "lorem.txt" changing expireDate to "+15" And user "Alice" should have a share with these details in the server: | field | value | | share_type | public_link | @@ -126,6 +124,8 @@ Feature: Share by public link | name | Public link | | expiration | +16 | + + @issue-ocis-1328 Scenario: user can set an expiry date when creating a public link to a date that is before the enforced max expiry date Given the setting "shareapi_default_expire_date" of app "core" has been set to "yes" in the server @@ -153,8 +153,7 @@ Feature: Share by public link | name | Public link | | expireDate | +5 | And user "Alice" has logged in using the webUI - When the user edits the public link named "Public link" of file "lorem.txt" changing following - | expireDate | +7 | + When the user edits the public link named "Public link" of file "lorem.txt" changing expireDate to "+7" Then user "Alice" should have a share with these details in the server: | field | value | | share_type | public_link | diff --git a/tests/acceptance/pageObjects/FilesPageElement/publicLinksDialog.js b/tests/acceptance/pageObjects/FilesPageElement/publicLinksDialog.js index e62881e3d6d..6db08631346 100644 --- a/tests/acceptance/pageObjects/FilesPageElement/publicLinksDialog.js +++ b/tests/acceptance/pageObjects/FilesPageElement/publicLinksDialog.js @@ -35,13 +35,70 @@ module.exports = { .click(linkRowEditButton) .waitForOutstandingAjaxCalls() }, + + clickLinkAddPasswordBtn: function (linkName) { + const linkRowAddPasswordSelector = + this.elements.publicLinkContainer.selector + + util.format(this.elements.publicLinkAddPasswordButton.selector, linkName) + const publicLinkAddPasswordButton = { + locateStrategy: this.elements.publicLinkAddPasswordButton.locateStrategy, + selector: linkRowAddPasswordSelector + } + return this.waitForElementVisible(publicLinkAddPasswordButton) + .initAjaxCounters() + .click(publicLinkAddPasswordButton) + .waitForOutstandingAjaxCalls() + }, + + clickLinkEditPasswordBtn: function (linkName) { + const linkRowEditPasswordSelector = + this.elements.publicLinkContainer.selector + + util.format(this.elements.publicLinkRenamePasswordButton.selector, linkName) + const publicLinkRenamePasswordButton = { + locateStrategy: this.elements.publicLinkRenamePasswordButton.locateStrategy, + selector: linkRowEditPasswordSelector + } + return this.waitForElementVisible(publicLinkRenamePasswordButton) + .initAjaxCounters() + .click(publicLinkRenamePasswordButton) + .waitForOutstandingAjaxCalls() + }, + + clickLinkEditNameBtn: function (linkName) { + const linkRowEditNameSelector = + this.elements.publicLinkContainer.selector + + util.format(this.elements.publicLinkRenameButton.selector, linkName) + const publicLinkRenameButton = { + locateStrategy: this.elements.publicLinkRenameButton.locateStrategy, + selector: linkRowEditNameSelector + } + return this.waitForElementVisible(publicLinkRenameButton) + .initAjaxCounters() + .click(publicLinkRenameButton) + .waitForOutstandingAjaxCalls() + }, + + clickLinkEditExpirationBtn: function (linkName) { + const linkRowEditExpirationDateSelector = + this.elements.publicLinkContainer.selector + + util.format(this.elements.publicLinkExpirationDateEditButton.selector, linkName) + const publicLinkExpirationDateEditButton = { + locateStrategy: this.elements.publicLinkExpirationDateEditButton.locateStrategy, + selector: linkRowEditExpirationDateSelector + } + return this.waitForElementVisible(publicLinkExpirationDateEditButton) + .initAjaxCounters() + .click(publicLinkExpirationDateEditButton) + .waitForOutstandingAjaxCalls() + }, + /** * sets role or permissions for public link on webUI * * @param {string} role - e.g. Viewer, Contributor, Editor, Uploader * @returns {Promise} */ - setPublicLinkRole: function (role) { + setPublicLinkInitialRole: function (role) { role = _(role).chain().toLower().startCase().replace(/\s/g, '').value() const selectedRoleDropdown = util.format( this.elements.publicLinkRoleSelectionDropdown.selector, @@ -89,7 +146,7 @@ module.exports = { */ setPublicLinkForm: async function (key, value) { if (key === 'role') { - return this.setPublicLinkRole(value) + return this.setPublicLinkInitialRole(value) } else if (key === 'name') { return this.setPublicLinkName(value) } else if (key === 'password') { @@ -115,11 +172,48 @@ module.exports = { * @param {string} editData.expireDate - Expire date for a public link share * @returns {exports} */ - editPublicLink: async function (linkName, editData) { + editPublicLink: async function (linkName) { await this.clickLinkEditBtn(linkName) - for (const [key, value] of Object.entries(editData)) { - await this.setPublicLinkForm(key, value) + return this + }, + + editPublicLinkExpiration: async function (linkName) { + await this.clickLinkEditExpirationBtn(linkName) + return this + }, + + changeExpirationDate: async function (linkName, expiry) { + const value = sharingHelper.calculateDate(expiry) + await this.editPublicLink(linkName) + await this.editPublicLinkExpiration(linkName) + return this.api.page.FilesPageElement.expirationDatePicker().setExpirationDate(value, 'link') + }, + + openRolesDrop: function (linkName) { + const linkRowEditRoleButtonSelector = + this.elements.publicLinkContainer.selector + + util.format(this.elements.publicLinkEditRoleButton.selector, linkName) + const linkRowEditRoleButton = { + locateStrategy: this.elements.publicLinkEditRoleButton.locateStrategy, + selector: linkRowEditRoleButtonSelector } + return this.waitForElementVisible(linkRowEditRoleButton) + .initAjaxCounters() + .click(linkRowEditRoleButton) + .waitForOutstandingAjaxCalls() + }, + + setPublicLinkRole: function (role) { + role = _(role).chain().toLower().startCase().replace(/\s/g, '').value() + return this.waitForElementVisible(`@role${role}`) + .initAjaxCounters() + .click(`@role${role}`) + .waitForOutstandingAjaxCalls() + }, + + changeRole: async function (linkName, role) { + await this.openRolesDrop(linkName) + await this.setPublicLinkRole(role) return this }, /** @@ -146,7 +240,7 @@ module.exports = { * @param {string} linkName Name of the public link share of a resource to be deleted * @returns {exports} */ - removePublicLink: function (linkName) { + removePublicLink: async function (linkName) { const linkRowDeleteButtonSelector = this.elements.publicLinkContainer.selector + util.format(this.elements.publicLinkDeleteButton.selector, linkName) @@ -154,6 +248,7 @@ module.exports = { locateStrategy: this.elements.publicLinkDeleteButton.locateStrategy, selector: linkRowDeleteButtonSelector } + await this.editPublicLink(linkName) return this.waitForElementVisible(linkRowDeleteButton) .initAjaxCounters() .click(linkRowDeleteButton) @@ -168,7 +263,7 @@ module.exports = { * @param {string} linkName Name of the public link share of a resource to be deleted * @returns {exports} */ - cancelRemovePublicLink: function (linkName) { + cancelRemovePublicLink: async function (linkName) { const linkRowDeleteButtonSelector = this.elements.publicLinkContainer.selector + util.format(this.elements.publicLinkDeleteButton.selector, linkName) @@ -176,6 +271,7 @@ module.exports = { locateStrategy: this.elements.publicLinkDeleteButton.locateStrategy, selector: linkRowDeleteButtonSelector } + await this.editPublicLink(linkName) return this.waitForElementVisible(linkRowDeleteButton) .initAjaxCounters() .click(linkRowDeleteButton) @@ -225,7 +321,6 @@ module.exports = { return this.waitForElementVisible('@publicLinkCreateButton') .initAjaxCounters() .click('@publicLinkCreateButton') - .waitForElementNotPresent('@publicLinkCreateButton') .waitForOutstandingAjaxCalls() }, /** @@ -296,7 +391,11 @@ module.exports = { } ) - if (attrElementId) { + // hack to check for presence of via-button + // since the redesign removed the visual via-text + if (attrElementId && attrName === 'viaLabel') { + linkResult.viaLabel = true + } else if (attrElementId) { await this.api.elementIdText(attrElementId, (text) => { linkResult[attrName] = text.value }) @@ -345,14 +444,82 @@ module.exports = { */ getErrorMessage: async function () { let message - const errorMessageXpath = - this.elements.publicLinkContainer.selector + - this.elements.errorMessageInsidePublicLinkContainer.selector - await this.getText('xpath', errorMessageXpath, function (result) { + await this.getText( + 'xpath', + this.elements.errorMessageInsidePublicLinkContainer.selector, + function (result) { + message = result.value + } + ) + console.log('\n\n', message, '\n\n') + return message + }, + + getErrorMessageFromModal: async function () { + let message + await this.getText('.oc-modal-body-input .oc-text-input-message', function (result) { message = result.value }) return message }, + + editPublicLinkName: async function (linkName) { + await this.clickLinkEditNameBtn(linkName) + return this + }, + + changeName: async function (linkName, newName) { + await this.editPublicLink(linkName) + await this.editPublicLinkName(linkName) + + await this.useXpath() + .waitForElementVisible('@dialog') + .waitForAnimationToFinish() + .clearValue('@dialogInput') + .setValue('@dialogInput', newName) + .useCss() + + await this.click('@dialogConfirmBtnEnabled') + }, + + editPublicLinkPassword: async function (linkName) { + await this.clickLinkEditPasswordBtn(linkName) + return this + }, + + addPublicLinkPassword: async function (linkName) { + await this.clickLinkAddPasswordBtn(linkName) + return this + }, + + addPassword: async function (linkName, password) { + await this.editPublicLink(linkName) + await this.addPublicLinkPassword(linkName) + + await this.useXpath() + .waitForElementVisible('@dialog') + .waitForAnimationToFinish() + .clearValue('@dialogInput') + .setValue('@dialogInput', password) + .useCss() + + await this.click('@dialogConfirmBtnEnabled') + }, + + changePassword: async function (linkName, password) { + await this.editPublicLink(linkName) + await this.editPublicLinkPassword(linkName) + + await this.useXpath() + .waitForElementVisible('@dialog') + .waitForAnimationToFinish() + .clearValue('@dialogInput') + .setValue('@dialogInput', password) + .useCss() + + await this.click('@dialogConfirmBtnEnabled') + }, + /** * clicks the 'copy-public-link-uri' button of a public link * @@ -416,7 +583,7 @@ module.exports = { selector: '.oc-files-file-link-name' }, publicLinkSubRole: { - selector: '.oc-files-file-link-role' + selector: '.link-details .oc-invisible-sr' }, publicLinkSubVia: { selector: '.oc-files-file-link-via' @@ -443,26 +610,51 @@ module.exports = { selector: '#files-role-uploader' }, errorMessageInsidePublicLinkContainer: { - selector: '//div[contains(@class, "oc-alert-danger")]', + selector: '//div[contains(@class, "oc-notification-message-warning")]', locateStrategy: 'xpath' }, publicLinkNameInputField: { selector: '//input[@id="oc-files-file-link-name"]', locateStrategy: 'xpath' }, + publicLinkEditRoleButton: { + selector: + '//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")]', + locateStrategy: 'xpath' + }, publicLinkEditButton: { selector: - '//h5[contains(@class, "oc-files-file-link-name") and text()="%s"]/../../..//button[contains(@class, "oc-files-file-link-edit")]', + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "details-buttons")]//button[contains(@class, "edit-drop-trigger")]', + locateStrategy: 'xpath' + }, + publicLinkRenameButton: { + selector: + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "details-buttons")]//button[text()="Rename"]', + locateStrategy: 'xpath' + }, + publicLinkAddPasswordButton: { + selector: + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "details-buttons")]//button[text()="Add password"]', + locateStrategy: 'xpath' + }, + publicLinkRenamePasswordButton: { + selector: + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "details-buttons")]//button[text()="Edit password"]', + locateStrategy: 'xpath' + }, + publicLinkExpirationDateEditButton: { + selector: + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "details-buttons")]//button[text()="Edit expiration date"]', locateStrategy: 'xpath' }, publicLinkDeleteButton: { selector: - '//h5[contains(@class, "oc-files-file-link-name") and text()="%s"]/../../..//button[contains(@class, "oc-files-file-link-delete")]', + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]//ancestor::li//div[contains(@class, "details-buttons")]//button[text()="Delete link"]', locateStrategy: 'xpath' }, publicLinkURLCopyButton: { selector: - '//h5[contains(@class, "oc-files-file-link-name") and text()="%s"]/../../..//button[contains(@class, "oc-files-public-link-copy-url")]', + '//h4[contains(@class, "oc-files-file-link-name") and text()="%s"]/../../..//button[contains(@class, "oc-files-public-link-copy-url")]', locateStrategy: 'xpath' }, publicLinkPasswordField: { @@ -485,8 +677,11 @@ module.exports = { dialog: { selector: '.oc-modal' }, + dialogInput: { + selector: '.oc-modal-body-input .oc-text-input' + }, dialogConfirmBtnEnabled: { - selector: '.oc-modal-body-actions-confirm:enabled' + selector: '.oc-modal-body-actions-confirm' }, dialogCancelBtn: { selector: '.oc-modal-body-actions-cancel' diff --git a/tests/acceptance/stepDefinitions/publicLinkContext.js b/tests/acceptance/stepDefinitions/publicLinkContext.js index b8ed1ce3be1..5e39a27d3af 100644 --- a/tests/acceptance/stepDefinitions/publicLinkContext.js +++ b/tests/acceptance/stepDefinitions/publicLinkContext.js @@ -99,11 +99,9 @@ const loadPublicLinkWithPassword = async function (linkCreator, password, newSes return client.page.publicLinkPasswordPage().submitPublicLinkPassword(password) } -const editPublicLink = async function (linkName, resource, dataTable) { - const editData = dataTable.rowsHash() +const editPublicLink = async function (linkName, resource) { await client.page.FilesPageElement.filesList().openPublicLinkDialog(resource) - await client.page.FilesPageElement.publicLinksDialog().editPublicLink(linkName, editData) - return client.page.FilesPageElement.publicLinksDialog().savePublicLink() + await client.page.FilesPageElement.publicLinksDialog().editPublicLink(linkName) } Then('the public should not get access to the publicly shared file', async function () { @@ -120,11 +118,10 @@ Then('the public should not get access to the publicly shared file', async funct }) When( - 'the user edits the public link named {string} of file/folder/resource {string} changing following but not saving', - async function (linkName, resource, dataTable) { - const editData = dataTable.rowsHash() + 'the user renames the public link named {string} of file/folder/resource {string} to {string}', + async function (linkName, resource, newName) { await client.page.FilesPageElement.filesList().openPublicLinkDialog(resource) - return client.page.FilesPageElement.publicLinksDialog().editPublicLink(linkName, editData) + await client.page.FilesPageElement.publicLinksDialog().changeName(linkName, newName) } ) @@ -135,11 +132,43 @@ When( } ) +When( + 'the user edits the public link named {string} of file/folder/resource {string} changing expireDate to {string}', + async function (linkName, resource, expiry) { + await client.page.FilesPageElement.filesList().openPublicLinkDialog(resource) + await client.page.FilesPageElement.publicLinksDialog().changeExpirationDate(linkName, expiry) + } +) + +When( + 'the user tries to edit the public link named {string} of file/folder/resource {string} changing the role to {string}', + async function (linkName, resource, role) { + await client.page.FilesPageElement.filesList().openPublicLinkDialog(resource) + await client.page.FilesPageElement.publicLinksDialog().changeRole(linkName, role) + } +) + +When( + 'the user tries to edit the public link named {string} of file/folder/resource {string} changing the password to {string}', + async function (linkName, resource, password) { + await client.page.FilesPageElement.filesList().openPublicLinkDialog(resource) + await client.page.FilesPageElement.publicLinksDialog().changePassword(linkName, password) + } +) + +When( + 'the user tries to edit the public link named {string} of file/folder/resource {string} adding a password {string}', + async function (linkName, resource, password) { + await client.page.FilesPageElement.filesList().openPublicLinkDialog(resource) + await client.page.FilesPageElement.publicLinksDialog().addPassword(linkName, password) + } +) + When( 'the user tries to edit the public link named {string} of file/folder/resource {string} changing following', - function (linkName, resource, dataTable) { + function (linkName, resource) { return ( - editPublicLink(linkName, resource, dataTable) + editPublicLink(linkName, resource) // while editing public link, after clicking the "Save" button, the button should disappear but if it doesn't // we throw "ElementPresentError" error. So, all the error except "ElementPresentError" is caught and thrown back // Also, when no error is thrown, the button seems to disappear, so an error should be thrown in such case as well. @@ -163,7 +192,7 @@ When( await api.publicLinksDialog().clickLinkEditBtn(linkName) const value = sharingHelper.calculateDate(pastDate) const dateToSet = new Date(Date.parse(value)) - await api.publicLinksDialog().openExpirationDatePicker() + await api.publicLinksDialog().clickLinkEditExpirationBtn(linkName) const isDisabled = await api.expirationDatePicker().isExpiryDateDisabled(dateToSet) return assert.ok(isDisabled, 'Expected expiration date to be disabled but found not disabled') } @@ -214,7 +243,7 @@ async function findMatchingPublicLinkByName(name, role, resource, via = null) { assert.strictEqual(role, share.role) if (via !== null) { - assert.strictEqual('Via ' + via, share.viaLabel) + assert.ok(share.viaLabel, 'Expected "shared via" icon to be displayed but was not visible') } } @@ -240,6 +269,15 @@ Then( } ) +Then( + 'the user should see an error message on the public link edit modal dialog saying {string}', + async function (expectedMessage) { + const actualMessage = + await client.page.FilesPageElement.publicLinksDialog().getErrorMessageFromModal() + return client.assert.strictEqual(actualMessage, expectedMessage) + } +) + When( 'the user copies the url of public link named {string} of file/folder/resource {string} using the webUI', async function (linkName, resource) { diff --git a/tests/e2e/support/objects/app-files/link/actions.ts b/tests/e2e/support/objects/app-files/link/actions.ts index c54bf77b1c2..713254d224c 100644 --- a/tests/e2e/support/objects/app-files/link/actions.ts +++ b/tests/e2e/support/objects/app-files/link/actions.ts @@ -70,6 +70,6 @@ export const createLink = async (args: createLinkArgs): Promise => { await page.locator('#oc-files-file-link-create').click() return await page - .locator(`//ul/li//h5[contains(text(),'${name}')]/following-sibling::div/a`) + .locator(`//ul/li/div/h4[contains(text(),'${name}')]/following-sibling::div//p`) .textContent() } diff --git a/yarn.lock b/yarn.lock index e71f9a27c16..adf6a256120 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2455,10 +2455,10 @@ __metadata: languageName: node linkType: hard -"@types/luxon@npm:^2.0.8": - version: 2.0.8 - resolution: "@types/luxon@npm:2.0.8" - checksum: 630d069a5a719c7512cb8805bcec6bac7f9aa5d434edf2159ba6dfb22eb8de982232a6d3621acd66f6ca2d1d6b0e8b40f9c8dafbbad654eb8cc01229f528ae45 +"@types/luxon@npm:^2.3.1": + version: 2.3.1 + resolution: "@types/luxon@npm:2.3.1" + checksum: e75928929aaf4e8796dcbdccd23a2dda029f637db01f5b06c7a72033ae106966df716e179ee41711280d782bd851228810643bce9bffc4ff62ae906e94ee1df2 languageName: node linkType: hard @@ -13816,7 +13816,7 @@ __metadata: dependencies: "@fortawesome/fontawesome-free": ^6.1.1 "@popperjs/core": ^2.4.0 - "@types/luxon": ^2.0.8 + "@types/luxon": ^2.3.1 "@types/semver": ^7.3.8 "@vue/composition-api": ^1.4.9 axios: ^0.26.1