diff --git a/cypress/integration/files-shares.spec.js b/cypress/integration/files-shares.spec.js new file mode 100644 index 000000000..32d95bf7f --- /dev/null +++ b/cypress/integration/files-shares.spec.js @@ -0,0 +1,208 @@ +/** + * @copyright Copyright (c) 2019 John Molakvoæ + * + * @author John Molakvoæ + * + * @license GNU AGPL version 3 or any later version + * + * 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 . + * + */ + +import { randHash } from '../utils/' +const randUser = randHash() + +describe('Files default view', function() { + before(function() { + // Init user + cy.nextcloudCreateUser(randUser, 'password') + cy.login(randUser, 'password') + + // Upload test files + cy.createFolder('Photos') + cy.uploadFile('image1.jpg', 'image/jpeg', '/Photos') + cy.uploadFile('image2.jpg', 'image/jpeg', '/Photos') + cy.uploadFile('image3.jpg', 'image/jpeg', '/Photos') + cy.uploadFile('image4.jpg', 'image/jpeg', '/Photos') + cy.uploadFile('video1.mp4', 'video/mp4', '/Photos') + cy.visit('/apps/files') + + // wait a bit for things to be settled + cy.wait(1000) + }) + after(function() { + // already logged out after visiting share link + // cy.logout() + }) + + it('See the default files list', function() { + cy.get('#fileList tr').should('contain', 'welcome.txt') + cy.get('#fileList tr').should('contain', 'Photos') + }) + + it('Does not have any visual regression 1', function() { + cy.matchImageSnapshot() + }) + + it('See shared files in the list', function() { + cy.openFile('Photos') + cy.get('#fileList tr[data-file="image1.jpg"]', { timeout: 10000 }) + .should('contain', 'image1.jpg') + cy.get('#fileList tr[data-file="image2.jpg"]', { timeout: 10000 }) + .should('contain', 'image2.jpg') + cy.get('#fileList tr[data-file="image3.jpg"]', { timeout: 10000 }) + .should('contain', 'image3.jpg') + cy.get('#fileList tr[data-file="image4.jpg"]', { timeout: 10000 }) + .should('contain', 'image4.jpg') + cy.get('#fileList tr[data-file="video1.mp4"]', { timeout: 10000 }) + .should('contain', 'video1.mp4') + }) + + it('Does not have any visual regression 2', function() { + cy.matchImageSnapshot() + }) + + it('Share the Photos folder with a share link and access the share link', function() { + cy.createLinkShare('/Photos').then(token => { + cy.logout() + cy.visit(`/s/${token}`) + }) + }) + + it('Does not have any visual regression 3', function() { + cy.matchImageSnapshot() + }) + + it('Open the viewer on file click', function() { + cy.openFile('image1.jpg') + cy.get('#viewer-content').should('be.visible') + }) + + it('Does not see a loading animation', function() { + cy.get('#viewer-content', { timeout: 4000 }) + .should('be.visible') + .and('have.class', 'modal-mask') + .and('not.have.class', 'icon-loading') + }) + + it('See the menu icon and title on the viewer header', function() { + cy.get('#viewer-content .modal-title').should('contain', 'image1.jpg') + cy.get('#viewer-content .modal-header button.icon-menu-sidebar-white-forced').should('not.be.visible') + cy.get('#viewer-content .modal-header button.icon-close').should('be.visible') + }) + + it('Does see next navigation arrows', function() { + cy.get('#viewer-content .modal-container img').should('have.length', 2) + cy.get('#viewer-content .modal-container img').should('have.attr', 'src') + cy.get('#viewer-content a.next').should('be.visible') + cy.get('#viewer-content a.next').should('be.visible') + }) + + it('Does not have any visual regression 4', function() { + cy.matchImageSnapshot() + }) + + it('Show image2 on next', function() { + cy.get('#viewer-content a.next').click() + cy.get('#viewer-content .modal-container img').should('have.length', 3) + cy.get('#viewer-content a.prev').should('be.visible') + cy.get('#viewer-content a.next').should('be.visible') + }) + + it('Does not see a loading animation', function() { + cy.get('#viewer-content', { timeout: 4000 }) + .should('be.visible') + .and('have.class', 'modal-mask') + .and('not.have.class', 'icon-loading') + }) + + it('Does not have any visual regression 5', function() { + cy.matchImageSnapshot() + }) + + it('Show image3 on next', function() { + cy.get('#viewer-content a.next').click() + cy.get('#viewer-content .modal-container img').should('have.length', 3) + cy.get('#viewer-content a.prev').should('be.visible') + cy.get('#viewer-content a.next').should('be.visible') + }) + + it('Does not see a loading animation', function() { + cy.get('#viewer-content', { timeout: 4000 }) + .should('be.visible') + .and('have.class', 'modal-mask') + .and('not.have.class', 'icon-loading') + }) + + it('Does not have any visual regression 6', function() { + cy.matchImageSnapshot() + }) + + it('Show image4 on next', function() { + cy.get('#viewer-content a.next').click() + cy.get('#viewer-content .modal-container img').should('have.length', 2) + cy.get('#viewer-content a.prev').should('be.visible') + cy.get('#viewer-content a.next').should('be.visible') + }) + + it('Does not see a loading animation', function() { + cy.get('#viewer-content', { timeout: 4000 }) + .should('be.visible') + .and('have.class', 'modal-mask') + .and('not.have.class', 'icon-loading') + }) + + it('Does not have any visual regression 7', function() { + cy.matchImageSnapshot() + }) + + it('Show video1 on next', function() { + cy.get('#viewer-content a.next').click() + // only 2 because we don't know if we're at the end of the slideshow, current vid and prev img + cy.get('#viewer-content .modal-container img').should('have.length', 1) + cy.get('#viewer-content .modal-container video').should('have.length', 1) + cy.get('#viewer-content a.prev').should('be.visible') + cy.get('#viewer-content a.next').should('be.visible') + cy.get('#viewer-content .modal-title').should('contain', 'video1.mp4') + }) + + it('Does not see a loading animation', function() { + cy.get('#viewer-content', { timeout: 4000 }) + .should('be.visible') + .and('have.class', 'modal-mask') + .and('not.have.class', 'icon-loading') + }) + + it('Does not have any visual regression 8', function() { + cy.matchImageSnapshot() + }) + + it('Show image1 again on next', function() { + cy.get('#viewer-content a.next').click() + cy.get('#viewer-content .modal-container img').should('have.length', 2) + cy.get('#viewer-content a.prev').should('be.visible') + cy.get('#viewer-content a.next').should('be.visible') + }) + + it('Does not see a loading animation', function() { + cy.get('#viewer-content', { timeout: 4000 }) + .should('be.visible') + .and('have.class', 'modal-mask') + .and('not.have.class', 'icon-loading') + }) + + it('Does not have any visual regression 9', function() { + cy.matchImageSnapshot() + }) +}) diff --git a/cypress/integration/images.spec.js b/cypress/integration/images.spec.js index c9c21b1c0..165f24616 100644 --- a/cypress/integration/images.spec.js +++ b/cypress/integration/images.spec.js @@ -73,6 +73,7 @@ describe('Open images in viewer', function() { }) it('Does see next navigation arrows', function() { + // only 2 because we don't know if we're at the end of the slideshow, current img and next one cy.get('#viewer-content .modal-container img').should('have.length', 2) cy.get('#viewer-content .modal-container img').should('have.attr', 'src') cy.get('#viewer-content a.next').should('be.visible') @@ -151,6 +152,7 @@ describe('Open images in viewer', function() { it('Show image4 on next', function() { cy.get('#viewer-content a.next').click() + // only 2 because we don't know if we're at the end of the slideshow, current img and previous one cy.get('#viewer-content .modal-container img').should('have.length', 2) cy.get('#viewer-content a.prev').should('be.visible') cy.get('#viewer-content a.next').should('be.visible') diff --git a/cypress/integration/videos.spec.js b/cypress/integration/videos.spec.js index 81b74752d..bbc2e0b9d 100644 --- a/cypress/integration/videos.spec.js +++ b/cypress/integration/videos.spec.js @@ -30,8 +30,8 @@ describe('Open mp4 videos in viewer', function() { cy.login(randUser, 'password') // Upload test file - cy.uploadFile('video1.mp4', 'image/jpeg') - cy.uploadFile('video2.mp4', 'image/jpeg') + cy.uploadFile('video1.mp4', 'video/mp4') + cy.uploadFile('video2.mp4', 'video/mp4') cy.visit('/apps/files') // wait a bit for things to be settled diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 2a1a7e19c..bcb4b15fc 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -22,6 +22,7 @@ import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command' import axios from '@nextcloud/axios' +import { generateOcsUrl } from '@nextcloud/router' addMatchImageSnapshotCommand() @@ -69,13 +70,13 @@ Cypress.Commands.add('nextcloudCreateUser', (user, password) => { }) }) -Cypress.Commands.add('uploadFile', (fileName, mimeType) => { +Cypress.Commands.add('uploadFile', (fileName, mimeType, path = '') => { cy.fixture(fileName, 'base64') .then(Cypress.Blob.base64StringToBlob) .then(async blob => { const file = new File([blob], fileName, { type: mimeType }) await cy.window().then(async window => { - await axios.put(`${Cypress.env('baseUrl')}/remote.php/webdav/${fileName}`, file, { + await axios.put(`${Cypress.env('baseUrl')}/remote.php/webdav${path}/${fileName}`, file, { headers: { requesttoken: window.OC.requestToken, 'Content-Type': mimeType @@ -110,10 +111,38 @@ Cypress.Commands.add('deleteFile', fileName => { cy.get(`#fileList tr[data-file="${fileName}"] a.name + .popovermenu .action-delete`).click() }) +/** + * Create a share link and return the share url + * + * @param {string} path the file/folder path + * @returns {string} the share link url + */ +Cypress.Commands.add('createLinkShare', path => { + return cy.window().then(async window => { + try { + const request = await axios.post(`${Cypress.env('baseUrl')}/ocs/v2.php/apps/files_sharing/api/v1/shares`, { + path, + shareType: window.OC.Share.SHARE_TYPE_LINK, + }, { + headers: { + requesttoken: window.OC.requestToken, + } + }) + if (!('ocs' in request.data) || !('token' in request.data.ocs.data && request.data.ocs.data.token.length > 0)) { + throw request + } + cy.log('Share link created', request.data.ocs.data.token) + return cy.wrap(request.data.ocs.data.token) + } catch(error) { + console.error(error) + } + }).should('have.length', 15) +}) + Cypress.Commands.overwrite('matchImageSnapshot', (originalFn, subject, name, options) => { // hide avatar because random colour break the visual regression tests - cy.window().then(win => { - const avatarDiv = win.document.querySelector('.avatardiv') + cy.window().then(window => { + const avatarDiv = window.document.querySelector('.avatardiv') if (avatarDiv) { avatarDiv.remove() }