diff options
author | Louis Chemineau <louis@chmn.me> | 2024-03-12 18:49:20 +0100 |
---|---|---|
committer | Louis Chemineau <louis@chmn.me> | 2024-03-14 11:07:20 +0100 |
commit | 01fe326df16bd479a0c75842b6baa22eebb9e279 (patch) | |
tree | 7baf22104fad86ea820d2bc08fed87c36f943fdb /cypress/e2e | |
parent | 2de9880d79adeeab0e0a01ce3992de404a0c274d (diff) | |
download | nextcloud-server-01fe326df16bd479a0c75842b6baa22eebb9e279.tar.gz nextcloud-server-01fe326df16bd479a0c75842b6baa22eebb9e279.zip |
test(files): Add e2e tests for live photo sync
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'cypress/e2e')
-rw-r--r-- | cypress/e2e/files/FilesUtils.ts | 31 | ||||
-rw-r--r-- | cypress/e2e/files/live_photos.cy.ts | 215 | ||||
-rw-r--r-- | cypress/e2e/files_sharing/filesSharingUtils.ts | 8 | ||||
-rw-r--r-- | cypress/e2e/files_versions/version_restoration.cy.ts | 2 |
4 files changed, 255 insertions, 1 deletions
diff --git a/cypress/e2e/files/FilesUtils.ts b/cypress/e2e/files/FilesUtils.ts index 7f61584bcde..798b9b5f60d 100644 --- a/cypress/e2e/files/FilesUtils.ts +++ b/cypress/e2e/files/FilesUtils.ts @@ -20,17 +20,31 @@ * */ +export const getRowForFileId = (fileid: number) => cy.get(`[data-cy-files-list-row-fileid="${fileid}"]`) export const getRowForFile = (filename: string) => cy.get(`[data-cy-files-list-row-name="${CSS.escape(filename)}"]`) +export const getActionsForFileId = (fileid: number) => getRowForFileId(fileid).find('[data-cy-files-list-row-actions]') export const getActionsForFile = (filename: string) => getRowForFile(filename).find('[data-cy-files-list-row-actions]') +export const getActionButtonForFileId = (fileid: number) => getActionsForFileId(fileid).find('button[aria-label="Actions"]') export const getActionButtonForFile = (filename: string) => getActionsForFile(filename).find('button[aria-label="Actions"]') +export const triggerActionForFileId = (fileid: number, actionId: string) => { + getActionButtonForFileId(fileid).click() + cy.get(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"] > button`).should('exist').click() +} export const triggerActionForFile = (filename: string, actionId: string) => { getActionButtonForFile(filename).click() cy.get(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"] > button`).should('exist').click() } +export const triggerInlineActionForFileId = (fileid: number, actionId: string) => { + getActionsForFileId(fileid).find(`button[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`).should('exist').click() +} +export const triggerInlineActionForFile = (filename: string, actionId: string) => { + getActionsForFile(filename).get(`button[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`).should('exist').click() +} + export const moveFile = (fileName: string, dirName: string) => { getRowForFile(fileName).should('be.visible') triggerActionForFile(fileName, 'move-copy') @@ -85,6 +99,23 @@ export const copyFile = (fileName: string, dirName: string) => { }) } +export const renameFile = (fileName: string, newFileName: string) => { + getRowForFile(fileName) + triggerActionForFile(fileName, 'rename') + + // intercept the move so we can wait for it + cy.intercept('MOVE', /\/remote.php\/dav\/files\//).as('moveFile') + + getRowForFile(fileName).find('[data-cy-files-list-row-name] input').clear() + getRowForFile(fileName).find('[data-cy-files-list-row-name] input').type(`${newFileName}{enter}`) + + cy.wait('@moveFile') +} + export const navigateToFolder = (folderName: string) => { getRowForFile(folderName).should('be.visible').find('[data-cy-files-list-row-name-link]').click() } + +export const closeSidebar = () => { + cy.get('[cy-data-sidebar] .app-sidebar__close').click() +} diff --git a/cypress/e2e/files/live_photos.cy.ts b/cypress/e2e/files/live_photos.cy.ts new file mode 100644 index 00000000000..98babb86941 --- /dev/null +++ b/cypress/e2e/files/live_photos.cy.ts @@ -0,0 +1,215 @@ +/** + * @copyright Copyright (c) 2024 Louis Chmn <louis@chmn.me> + * + * @author Louis Chmn <louis@chmn.me> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import type { User } from '@nextcloud/cypress' +import { closeSidebar, copyFile, getRowForFile, getRowForFileId, renameFile, triggerActionForFile, triggerInlineActionForFileId } from './FilesUtils' + +/** + * + * @param label + */ +function refreshView(label: string) { + cy.intercept('PROPFIND', /\/remote.php\/dav\//).as('propfind') + cy.get('[data-cy-files-content-breadcrumbs]').contains(label).click() + cy.wait('@propfind') +} + +/** + * + * @param user + * @param fileName + * @param domain + * @param requesttoken + * @param metadata + */ +function setMetadata(user: User, fileName: string, domain: string, requesttoken: string, metadata: object) { + cy.request({ + method: 'PROPPATCH', + url: `http://${domain}/remote.php/dav/files/${user.userId}/${fileName}`, + auth: { user: user.userId, pass: user.password }, + headers: { + requesttoken, + }, + body: `<?xml version="1.0"?> + <d:propertyupdate xmlns:d="DAV:" xmlns:nc="http://nextcloud.org/ns"> + <d:set> + <d:prop> + ${Object.entries(metadata).map(([key, value]) => `<${key}>${value}</${key}>`).join('\n')} + </d:prop> + </d:set> + </d:propertyupdate>`, + }) +} + +describe('Files: Live photos', { testIsolation: true }, () => { + let currentUser: User + let randomFileName: string + let jpgFileId: number + let movFileId: number + let hostname: string + let requesttoken: string + + before(() => { + cy.createRandomUser().then((user) => { + currentUser = user + cy.login(currentUser) + cy.visit('/apps/files') + }) + + cy.url().then(url => { hostname = new URL(url).hostname }) + }) + + beforeEach(() => { + randomFileName = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 10) + + cy.uploadContent(currentUser, new Blob(['jpg file'], { type: 'image/jpg' }), 'image/jpg', `/${randomFileName}.jpg`) + .then(response => { jpgFileId = parseInt(response.headers['oc-fileid']) }) + cy.uploadContent(currentUser, new Blob(['mov file'], { type: 'video/mov' }), 'video/mov', `/${randomFileName}.mov`) + .then(response => { movFileId = parseInt(response.headers['oc-fileid']) }) + + cy.login(currentUser) + cy.visit('/apps/files') + + cy.get('head').invoke('attr', 'data-requesttoken').then(_requesttoken => { requesttoken = _requesttoken as string }) + + cy.then(() => { + setMetadata(currentUser, `${randomFileName}.jpg`, hostname, requesttoken, { 'nc:metadata-files-live-photo': movFileId }) + setMetadata(currentUser, `${randomFileName}.mov`, hostname, requesttoken, { 'nc:metadata-files-live-photo': jpgFileId }) + }) + + cy.then(() => { + cy.visit(`/apps/files/files/${jpgFileId}`) // Refresh and scroll to the .jpg file. + closeSidebar() + }) + }) + + it('Only renders the .jpg file', () => { + getRowForFileId(jpgFileId).should('have.length', 1) + getRowForFileId(movFileId).should('have.length', 0) + }) + + context("'Show hidden files' is enabled", () => { + before(() => { + cy.login(currentUser) + cy.visit('/apps/files') + cy.get('[data-cy-files-navigation-settings-button]').click() + // Force:true because the checkbox is hidden by the pretty UI. + cy.get('[data-cy-files-settings-setting="show_hidden"] input').check({ force: true }) + }) + + it("Shows both files when 'Show hidden files' is enabled", () => { + getRowForFileId(jpgFileId).should('have.length', 1).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}.jpg`) + getRowForFileId(movFileId).should('have.length', 1).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}.mov`) + }) + + it('Copies both files when copying the .jpg', () => { + copyFile(`${randomFileName}.jpg`, '.') + refreshView('All files') + + getRowForFile(`${randomFileName}.jpg`).should('have.length', 1) + getRowForFile(`${randomFileName}.mov`).should('have.length', 1) + getRowForFile(`${randomFileName} (copy).jpg`).should('have.length', 1) + getRowForFile(`${randomFileName} (copy).mov`).should('have.length', 1) + }) + + it('Copies both files when copying the .mov', () => { + copyFile(`${randomFileName}.mov`, '.') + refreshView('All files') + + getRowForFile(`${randomFileName}.mov`).should('have.length', 1) + getRowForFile(`${randomFileName} (copy).jpg`).should('have.length', 1) + getRowForFile(`${randomFileName} (copy).mov`).should('have.length', 1) + }) + + it('Moves files when moving the .jpg', () => { + renameFile(`${randomFileName}.jpg`, `${randomFileName}_moved.jpg`) + refreshView('All files') + + getRowForFileId(jpgFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.jpg`) + getRowForFileId(movFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.mov`) + }) + + it('Moves files when moving the .mov', () => { + renameFile(`${randomFileName}.mov`, `${randomFileName}_moved.mov`) + refreshView('All files') + + getRowForFileId(jpgFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.jpg`) + getRowForFileId(movFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.mov`) + }) + + it('Deletes files when deleting the .jpg', () => { + triggerActionForFile(`${randomFileName}.jpg`, 'delete') + refreshView('All files') + + getRowForFile(`${randomFileName}.jpg`).should('have.length', 0) + getRowForFile(`${randomFileName}.mov`).should('have.length', 0) + + cy.visit('/apps/files/trashbin') + + getRowForFileId(jpgFileId).invoke('attr', 'data-cy-files-list-row-name').should('to.match', new RegExp(`^${randomFileName}.jpg\\.d[0-9]+$`)) + getRowForFileId(movFileId).invoke('attr', 'data-cy-files-list-row-name').should('to.match', new RegExp(`^${randomFileName}.mov\\.d[0-9]+$`)) + }) + + it('Block deletion when deleting the .mov', () => { + triggerActionForFile(`${randomFileName}.mov`, 'delete') + refreshView('All files') + + getRowForFile(`${randomFileName}.jpg`).should('have.length', 1) + getRowForFile(`${randomFileName}.mov`).should('have.length', 1) + + cy.visit('/apps/files/trashbin') + + getRowForFileId(jpgFileId).should('have.length', 0) + getRowForFileId(movFileId).should('have.length', 0) + }) + + it('Restores files when restoring the .jpg', () => { + triggerActionForFile(`${randomFileName}.jpg`, 'delete') + cy.visit('/apps/files/trashbin') + triggerInlineActionForFileId(jpgFileId, 'restore') + refreshView('Deleted files') + + getRowForFile(`${randomFileName}.jpg`).should('have.length', 0) + getRowForFile(`${randomFileName}.mov`).should('have.length', 0) + + cy.visit('/apps/files') + + getRowForFile(`${randomFileName}.jpg`).should('have.length', 1) + getRowForFile(`${randomFileName}.mov`).should('have.length', 1) + }) + + it('Blocks restoration when restoring the .mov', () => { + triggerActionForFile(`${randomFileName}.jpg`, 'delete') + cy.visit('/apps/files/trashbin') + triggerInlineActionForFileId(movFileId, 'restore') + refreshView('Deleted files') + + getRowForFileId(jpgFileId).should('have.length', 1) + getRowForFileId(movFileId).should('have.length', 1) + + cy.visit('/apps/files') + + getRowForFile(`${randomFileName}.jpg`).should('have.length', 0) + getRowForFile(`${randomFileName}.mov`).should('have.length', 0) + }) + }) +}) diff --git a/cypress/e2e/files_sharing/filesSharingUtils.ts b/cypress/e2e/files_sharing/filesSharingUtils.ts index ee80041b619..cb407153380 100644 --- a/cypress/e2e/files_sharing/filesSharingUtils.ts +++ b/cypress/e2e/files_sharing/filesSharingUtils.ts @@ -58,8 +58,10 @@ export function updateShare(fileName: string, index: number, shareSettings: Part if (shareSettings.download !== undefined) { cy.get('[data-cy-files-sharing-share-permissions-checkbox="download"]').find('input').as('downloadCheckbox') if (shareSettings.download) { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@downloadCheckbox').check({ force: true, scrollBehavior: 'nearest' }) } else { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@downloadCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' }) } } @@ -67,8 +69,10 @@ export function updateShare(fileName: string, index: number, shareSettings: Part if (shareSettings.read !== undefined) { cy.get('[data-cy-files-sharing-share-permissions-checkbox="read"]').find('input').as('readCheckbox') if (shareSettings.read) { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@readCheckbox').check({ force: true, scrollBehavior: 'nearest' }) } else { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@readCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' }) } } @@ -76,8 +80,10 @@ export function updateShare(fileName: string, index: number, shareSettings: Part if (shareSettings.update !== undefined) { cy.get('[data-cy-files-sharing-share-permissions-checkbox="update"]').find('input').as('updateCheckbox') if (shareSettings.update) { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@updateCheckbox').check({ force: true, scrollBehavior: 'nearest' }) } else { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@updateCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' }) } } @@ -85,8 +91,10 @@ export function updateShare(fileName: string, index: number, shareSettings: Part if (shareSettings.delete !== undefined) { cy.get('[data-cy-files-sharing-share-permissions-checkbox="delete"]').find('input').as('deleteCheckbox') if (shareSettings.delete) { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@deleteCheckbox').check({ force: true, scrollBehavior: 'nearest' }) } else { + // Force:true because the checkbox is hidden by the pretty UI. cy.get('@deleteCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' }) } } diff --git a/cypress/e2e/files_versions/version_restoration.cy.ts b/cypress/e2e/files_versions/version_restoration.cy.ts index c5dbaeab964..d9d983b0d09 100644 --- a/cypress/e2e/files_versions/version_restoration.cy.ts +++ b/cypress/e2e/files_versions/version_restoration.cy.ts @@ -113,7 +113,7 @@ describe('Versions restoration', () => { auth: { user: recipient.userId, pass: recipient.password }, headers: { cookie: '', - Destination: 'https://nextcloud_server1.test/remote.php/dav/versions/admin/restore/target', + Destination: `http://${hostname}/remote.php/dav/versions/${recipient.userId}/restore/target`, }, url: `http://${hostname}/remote.php/dav/versions/${recipient.userId}/versions/${fileId}/${versionId}`, failOnStatusCode: false, |