mirror of https://github.com/nextcloud/photos
Copy how cypress is run on the server
Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
parent
efa2bf5471
commit
e66d96dc34
|
@ -10,8 +10,6 @@ on:
|
|||
env:
|
||||
APP_NAME: photos
|
||||
BRANCH: ${{ github.base_ref }}
|
||||
CYPRESS_baseUrl: http://127.0.0.1:8082/index.php
|
||||
TESTING: true
|
||||
|
||||
jobs:
|
||||
init:
|
||||
|
@ -22,15 +20,16 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
|
||||
- name: Read package.json node and npm engines version
|
||||
uses: skjnldsv/read-package-engines-version-actions@v1.1
|
||||
uses: skjnldsv/read-package-engines-version-actions@v2.0
|
||||
id: versions
|
||||
with:
|
||||
fallbackNode: '^12'
|
||||
fallbackNpm: '^6'
|
||||
fallbackNode: "^14"
|
||||
fallbackNpm: "^7"
|
||||
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "npm"
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
|
||||
|
@ -40,7 +39,6 @@ jobs:
|
|||
run: |
|
||||
npm ci
|
||||
TESTING=true npm run build --if-present
|
||||
|
||||
- name: Save context
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
|
@ -55,7 +53,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
# run multiple copies of the current job in parallel
|
||||
containers: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
containers: ["component", 1, 2]
|
||||
|
||||
name: runner ${{ matrix.containers }}
|
||||
|
||||
|
@ -66,21 +64,9 @@ jobs:
|
|||
key: cypress-context-${{ github.run_id }}
|
||||
path: /home/runner/work/photos
|
||||
|
||||
- name: Setup server
|
||||
run: |
|
||||
cd cypress
|
||||
docker-compose up -d
|
||||
- name: Run ${{ matrix.containers == 'component' && 'component' || 'E2E' }} cypress tests
|
||||
|
||||
- name: Wait for server
|
||||
run: npm run wait-on $CYPRESS_baseUrl
|
||||
|
||||
- name: Enable app & configure server
|
||||
run: |
|
||||
cd cypress
|
||||
docker-compose exec --env APP_NAME=${{ env.APP_NAME }} --env BRANCH=${{ env.BRANCH }} -T nextcloud bash /initserver.sh
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v4
|
||||
uses: cypress-io/github-action@v5
|
||||
with:
|
||||
record: true
|
||||
parallel: true
|
||||
|
@ -88,10 +74,14 @@ jobs:
|
|||
ci-build-id: ${{ github.sha }}-${{ github.run_number }}
|
||||
tag: ${{ github.event_name }}
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
# Needs to be prefixed with CYPRESS_
|
||||
CYPRESS_BRANCH: ${{ env.BRANCH }}
|
||||
# https://github.com/cypress-io/github-action/issues/124
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }}
|
||||
# Needed for some specific code workarounds
|
||||
TESTING: true
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
|
||||
- name: Upload snapshots
|
||||
uses: actions/upload-artifact@v3
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
const { defineConfig } = require("cypress");
|
||||
const browserify = require('@cypress/browserify-preprocessor')
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: 'okzqgr',
|
||||
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
defaultCommandTimeout: 6000,
|
||||
retries: 1,
|
||||
|
||||
env: {
|
||||
failSilently: false,
|
||||
type: 'actual',
|
||||
},
|
||||
|
||||
screenshotsFolder: 'cypress/snapshots/actual',
|
||||
trashAssetsBeforeRuns: true,
|
||||
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:8082/index.php',
|
||||
|
||||
setupNodeEvents(on, config) {
|
||||
// Fix browserslist extend https://github.com/cypress-io/cypress/issues/2983#issuecomment-570616682
|
||||
on('file:preprocessor', browserify())
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
import {
|
||||
configureNextcloud,
|
||||
startNextcloud,
|
||||
stopNextcloud,
|
||||
waitOnNextcloud,
|
||||
} from './cypress/dockerNode'
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
import browserify from '@cypress/browserify-preprocessor'
|
||||
import getCompareSnapshotsPlugin from 'cypress-visual-regression/dist/plugin'
|
||||
|
||||
export default defineConfig({
|
||||
projectId: 'okzqgr',
|
||||
|
||||
// 16/9 screen ratio
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
|
||||
// Tries again 2 more times on failure
|
||||
retries: {
|
||||
runMode: 2,
|
||||
// do not retry in `cypress open`
|
||||
openMode: 0,
|
||||
},
|
||||
|
||||
// Needed to trigger `after:run` events with cypress open
|
||||
experimentalInteractiveRunEvents: true,
|
||||
|
||||
// faster video processing
|
||||
videoCompression: false,
|
||||
|
||||
// Visual regression testing
|
||||
env: {
|
||||
failSilently: false,
|
||||
type: 'actual',
|
||||
},
|
||||
screenshotsFolder: 'cypress/snapshots/actual',
|
||||
trashAssetsBeforeRuns: true,
|
||||
|
||||
e2e: {
|
||||
testIsolation: false,
|
||||
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
async setupNodeEvents(on, config) {
|
||||
// Fix browserslist extend https://github.com/cypress-io/cypress/issues/2983#issuecomment-570616682
|
||||
on('file:preprocessor', browserify({ typescript: require.resolve('typescript') }))
|
||||
getCompareSnapshotsPlugin(on, config)
|
||||
|
||||
// Disable spell checking to prevent rendering differences
|
||||
on('before:browser:launch', (browser, launchOptions) => {
|
||||
if (browser.family === 'chromium' && browser.name !== 'electron') {
|
||||
launchOptions.preferences.default['browser.enable_spellchecking'] = false
|
||||
return launchOptions
|
||||
}
|
||||
|
||||
if (browser.family === 'firefox') {
|
||||
launchOptions.preferences['layout.spellcheckDefault'] = 0
|
||||
return launchOptions
|
||||
}
|
||||
|
||||
if (browser.name === 'electron') {
|
||||
launchOptions.preferences.spellcheck = false
|
||||
return launchOptions
|
||||
}
|
||||
})
|
||||
|
||||
// Remove container after run
|
||||
on('after:run', () => {
|
||||
stopNextcloud()
|
||||
})
|
||||
|
||||
// Before the browser launches
|
||||
// starting Nextcloud testing container
|
||||
return startNextcloud(process.env.BRANCH)
|
||||
.then((ip) => {
|
||||
// Setting container's IP as base Url
|
||||
config.baseUrl = `http://${ip}/index.php`
|
||||
return ip
|
||||
})
|
||||
.then(waitOnNextcloud)
|
||||
.then(() => configureNextcloud(process.env.BRANCH))
|
||||
.then(() => {
|
||||
return config
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
|
@ -0,0 +1,224 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable n/no-unpublished-import */
|
||||
/* eslint-disable n/no-extraneous-import */
|
||||
|
||||
import Docker from 'dockerode'
|
||||
import path from 'path'
|
||||
import waitOn from 'wait-on'
|
||||
|
||||
import pkg from '../package.json'
|
||||
|
||||
export const docker = new Docker()
|
||||
|
||||
const APP_PATH = path.resolve(__dirname, '../')
|
||||
const APP_NAME = pkg.name
|
||||
|
||||
const CONTAINER_NAME = 'nextcloud-cypress-tests-' + APP_NAME
|
||||
const SERVER_IMAGE = 'ghcr.io/nextcloud/continuous-integration-shallow-server'
|
||||
|
||||
/**
|
||||
* Start the testing container
|
||||
*/
|
||||
export const startNextcloud = async function(branch: string = 'master'): Promise<any> {
|
||||
|
||||
try {
|
||||
// Pulling images
|
||||
console.log('\nPulling images... ⏳')
|
||||
await new Promise((resolve, reject): any => docker.pull(SERVER_IMAGE, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
// https://github.com/apocas/dockerode/issues/357
|
||||
docker.modem.followProgress(stream, onFinished)
|
||||
|
||||
/**
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
function onFinished(err) {
|
||||
if (!err) {
|
||||
resolve(true)
|
||||
return
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
}))
|
||||
console.log('└─ Done')
|
||||
|
||||
// Remove old container if exists
|
||||
console.log('\nChecking running containers... 🔍')
|
||||
try {
|
||||
const oldContainer = docker.getContainer(CONTAINER_NAME)
|
||||
const oldContainerData = await oldContainer.inspect()
|
||||
if (oldContainerData) {
|
||||
console.log('├─ Existing running container found')
|
||||
console.log('├─ Removing... ⏳')
|
||||
// Forcing any remnants to be removed just in case
|
||||
await oldContainer.remove({ force: true })
|
||||
console.log('└─ Done')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('└─ None found!')
|
||||
}
|
||||
|
||||
// Starting container
|
||||
console.log('\nStarting Nextcloud container... 🚀')
|
||||
console.log(`├─ Using branch '${branch}'`)
|
||||
console.log(`├─ And binding app '${APP_NAME}' from '${APP_PATH}'`)
|
||||
const container = await docker.createContainer({
|
||||
Image: SERVER_IMAGE,
|
||||
name: CONTAINER_NAME,
|
||||
HostConfig: {
|
||||
Binds: [
|
||||
// TODO: improve local app directory detection
|
||||
`${APP_PATH}/:/var/www/html/apps/${APP_NAME}`,
|
||||
],
|
||||
},
|
||||
Env: [
|
||||
`BRANCH=${branch}`,
|
||||
],
|
||||
})
|
||||
await container.start()
|
||||
|
||||
// Get container's IP
|
||||
const ip = await getContainerIP(container)
|
||||
|
||||
console.log(`├─ Nextcloud container's IP is ${ip} 🌏`)
|
||||
return ip
|
||||
} catch (err) {
|
||||
console.log('└─ Unable to start the container 🛑')
|
||||
console.log(err)
|
||||
stopNextcloud()
|
||||
throw new Error('Unable to start the container')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Nextcloud
|
||||
*/
|
||||
export const configureNextcloud = async function(branch: string = 'master') {
|
||||
console.log('\nConfiguring nextcloud...')
|
||||
const container = docker.getContainer(CONTAINER_NAME)
|
||||
await runExec(container, ['php', 'occ', '--version'], true)
|
||||
|
||||
// Clone the viewer app
|
||||
await runExec(container, ['git', 'clone', '--depth', '1', '--branch', branch, 'https://github.com/nextcloud/viewer.git', '/var/www/html/apps/viewer'], true)
|
||||
|
||||
// Be consistent for screenshots
|
||||
await runExec(container, ['php', 'occ', 'config:system:set', 'default_language', '--value', 'en'], true)
|
||||
await runExec(container, ['php', 'occ', 'config:system:set', 'force_language', '--value', 'en'], true)
|
||||
await runExec(container, ['php', 'occ', 'config:system:set', 'default_locale', '--value', 'en_US'], true)
|
||||
await runExec(container, ['php', 'occ', 'config:system:set', 'force_locale', '--value', 'en_US'], true)
|
||||
await runExec(container, ['php', 'occ', 'config:system:set', 'enforce_theme', '--value', 'light'], true)
|
||||
|
||||
console.log('└─ Nextcloud is now ready to use 🎉')
|
||||
}
|
||||
|
||||
/**
|
||||
* Force stop the testing container
|
||||
*/
|
||||
export const stopNextcloud = async function() {
|
||||
try {
|
||||
const container = docker.getContainer(CONTAINER_NAME)
|
||||
console.log('Stopping Nextcloud container...')
|
||||
container.remove({ force: true })
|
||||
console.log('└─ Nextcloud container removed 🥀')
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the testing container's IP
|
||||
*/
|
||||
export const getContainerIP = async function(
|
||||
container: Docker.Container = docker.getContainer(CONTAINER_NAME)
|
||||
): Promise<string> {
|
||||
let ip = ''
|
||||
let tries = 0
|
||||
while (ip === '' && tries < 10) {
|
||||
tries++
|
||||
|
||||
await container.inspect(function(err, data) {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
ip = data?.NetworkSettings?.IPAddress || ''
|
||||
})
|
||||
|
||||
if (ip !== '') {
|
||||
break
|
||||
}
|
||||
|
||||
await sleep(1000 * tries)
|
||||
}
|
||||
|
||||
return ip
|
||||
}
|
||||
|
||||
// Would be simpler to start the container from cypress.config.ts,
|
||||
// but when checking out different branches, it can take a few seconds
|
||||
// Until we can properly configure the baseUrl retry intervals,
|
||||
// We need to make sure the server is already running before cypress
|
||||
// https://github.com/cypress-io/cypress/issues/22676
|
||||
export const waitOnNextcloud = async function(ip: string) {
|
||||
console.log('├─ Waiting for Nextcloud to be ready... ⏳')
|
||||
await waitOn({ resources: [`http://${ip}/index.php`] })
|
||||
console.log('└─ Done')
|
||||
}
|
||||
|
||||
const runExec = async function(
|
||||
container: Docker.Container,
|
||||
command: string[],
|
||||
verbose = false,
|
||||
user = 'www-data'
|
||||
) {
|
||||
const exec = await container.exec({
|
||||
Cmd: command,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
User: user,
|
||||
})
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec.start({}, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
if (stream) {
|
||||
stream.setEncoding('utf-8')
|
||||
stream.on('data', str => {
|
||||
if (verbose && str.trim() !== '') {
|
||||
console.log(`├─ ${str.trim().replace(/\n/gi, '\n├─ ')}`)
|
||||
}
|
||||
})
|
||||
stream.on('end', resolve)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const sleep = function(milliseconds: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, milliseconds))
|
||||
}
|
|
@ -19,116 +19,126 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import { randHash } from '../utils'
|
||||
const randUser = randHash()
|
||||
import {
|
||||
addFilesToAlbumFromAlbum,
|
||||
createAnAlbumFromAlbums,
|
||||
deleteAnAlbumFromAlbumContent,
|
||||
removeSelectionFromAlbum,
|
||||
} from './albumsUtils'
|
||||
import {
|
||||
downloadSelection,
|
||||
favoriteSelection,
|
||||
selectMedia,
|
||||
unfavoriteSelection,
|
||||
unselectMedia,
|
||||
uploadTestMedia,
|
||||
} from './photosUtils'
|
||||
|
||||
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false
|
||||
}
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
describe('Manage albums', () => {
|
||||
before(function () {
|
||||
cy.logout()
|
||||
cy.nextcloudCreateUser(randUser, 'password')
|
||||
before(function() {
|
||||
cy.createRandomUser()
|
||||
.then((user) => {
|
||||
uploadTestMedia(user)
|
||||
cy.login(user)
|
||||
cy.visit('/apps/photos')
|
||||
})
|
||||
})
|
||||
|
||||
cy.login(randUser, 'password')
|
||||
cy.uploadTestMedia()
|
||||
beforeEach(() => {
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
createAnAlbumFromAlbums('albums_test')
|
||||
addFilesToAlbumFromAlbum('albums_test', [0, 1, 2])
|
||||
})
|
||||
|
||||
// wait a bit for things to be settled
|
||||
cy.wait(1000)
|
||||
})
|
||||
afterEach(() => {
|
||||
deleteAnAlbumFromAlbumContent()
|
||||
cy.contains('There is no album yet!').click()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
cy.createAnAlbumFromAlbums('albums_test')
|
||||
cy.addFilesToAlbumFromAlbum('albums_test', [0, 1, 2])
|
||||
})
|
||||
it('Add and remove a file to an album from an album', () => {
|
||||
selectMedia([0])
|
||||
removeSelectionFromAlbum()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.deleteAnAlbumFromAlbumContent()
|
||||
cy.contains("There is no album yet!").click()
|
||||
})
|
||||
it('Add and remove multiple files to an album from an album', () => {
|
||||
selectMedia([0, 1])
|
||||
removeSelectionFromAlbum()
|
||||
})
|
||||
|
||||
it('Add and remove a file to an album from an album', () => {
|
||||
cy.selectMedia([0])
|
||||
cy.removeSelectionFromAlbum()
|
||||
})
|
||||
it('Favorite a file from an album', () => {
|
||||
selectMedia([0])
|
||||
favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(0).find('[aria-label="The file is in the favorites"]')
|
||||
unfavoriteSelection()
|
||||
unselectMedia([0])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
|
||||
it('Add and remove multiple files to an album from an album', () => {
|
||||
cy.selectMedia([0, 1])
|
||||
cy.removeSelectionFromAlbum()
|
||||
})
|
||||
it('Favorite multiple files from an album', () => {
|
||||
selectMedia([1, 2])
|
||||
favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(1).find('[aria-label="The file is in the favorites"]')
|
||||
cy.get('[data-test="media"]').eq(2).find('[aria-label="The file is in the favorites"]')
|
||||
unfavoriteSelection()
|
||||
unselectMedia([1, 2])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
|
||||
it('Favorite a file from an album', () => {
|
||||
cy.selectMedia([0])
|
||||
cy.favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(0).find('[aria-label="The file is in the favorites"]')
|
||||
cy.unfavoriteSelection()
|
||||
cy.unselectMedia([0])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
it('Download a file from an album', () => {
|
||||
selectMedia([0])
|
||||
downloadSelection()
|
||||
unselectMedia([0])
|
||||
})
|
||||
|
||||
it('Favorite multiple files from an album', () => {
|
||||
cy.selectMedia([1, 2])
|
||||
cy.favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(1).find('[aria-label="The file is in the favorites"]')
|
||||
cy.get('[data-test="media"]').eq(2).find('[aria-label="The file is in the favorites"]')
|
||||
cy.unfavoriteSelection()
|
||||
cy.unselectMedia([1, 2])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
it('Download multiple files from an album', () => {
|
||||
selectMedia([1, 2])
|
||||
downloadSelection()
|
||||
unselectMedia([1, 2])
|
||||
})
|
||||
|
||||
it('Download a file from an album', () => {
|
||||
cy.selectMedia([0])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([0])
|
||||
})
|
||||
it('Download all files from an album', () => {
|
||||
selectMedia([1, 2])
|
||||
downloadSelection()
|
||||
unselectMedia([1, 2])
|
||||
})
|
||||
|
||||
it('Download multiple files from an album', () => {
|
||||
cy.selectMedia([1, 2])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([1, 2])
|
||||
})
|
||||
it('Edit an album\'s name', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="name"]').clear().type('New name')
|
||||
cy.contains('Save').click()
|
||||
|
||||
it('Download all files from an album', () => {
|
||||
cy.selectMedia([1, 2])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([1, 2])
|
||||
})
|
||||
cy.reload()
|
||||
|
||||
it('Edit an album\'s name', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="name"]').clear().type("New name")
|
||||
cy.contains('Save').click()
|
||||
cy.contains('New name')
|
||||
|
||||
cy.reload()
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="name"]').clear().type('albums_test')
|
||||
cy.contains('Save').click()
|
||||
})
|
||||
|
||||
cy.contains('New name')
|
||||
it('Edit an album\'s location', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="location"]').clear().type('New location')
|
||||
cy.contains('Save').click()
|
||||
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="name"]').clear().type("albums_test")
|
||||
cy.contains('Save').click()
|
||||
})
|
||||
cy.reload()
|
||||
|
||||
it('Edit an album\'s location', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="location"]').clear().type("New location")
|
||||
cy.contains('Save').click()
|
||||
cy.contains('New location')
|
||||
|
||||
cy.reload()
|
||||
|
||||
cy.contains('New location')
|
||||
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="location"]').clear()
|
||||
cy.contains('Save').click()
|
||||
})
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Edit album details').click()
|
||||
cy.get('form [name="location"]').clear()
|
||||
cy.contains('Save').click()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 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 { selectMedia } from "./photosUtils"
|
||||
|
||||
export function createAnAlbumFromTimeline(albumName: string) {
|
||||
cy.contains('Add').click()
|
||||
cy.contains('Create new album').click()
|
||||
cy.get('form [name="name"]').type(albumName)
|
||||
cy.contains('Create album').click()
|
||||
}
|
||||
|
||||
export function createAnAlbumFromAlbums(albumName: string) {
|
||||
cy.contains('New album').click()
|
||||
cy.get('form [name="name"]').type(albumName)
|
||||
cy.contains('Create album').click()
|
||||
}
|
||||
|
||||
export function deleteAnAlbumFromAlbumContent() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Delete album').click()
|
||||
}
|
||||
|
||||
export function addFilesToAlbumFromTimeline(albumName: string) {
|
||||
cy.contains('Add to album').click()
|
||||
cy.get('.album-picker ul').contains(albumName).click()
|
||||
}
|
||||
|
||||
export function addFilesToAlbumFromAlbum(albumName: string, itemsIndex: number[]) {
|
||||
cy.get('[aria-label="Add photos to this album"]').click()
|
||||
cy.get('.file-picker__file-list').within(() => {
|
||||
selectMedia(itemsIndex)
|
||||
})
|
||||
cy.contains(`Add to ${albumName}`).click()
|
||||
}
|
||||
|
||||
export function removeSelectionFromAlbum() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Remove selection from album').click()
|
||||
}
|
||||
|
||||
export function goToAlbum(albumName: string) {
|
||||
cy.get('.app-navigation__list').contains('Albums').click()
|
||||
cy.get('ul.collections__list').contains(albumName).click()
|
||||
}
|
||||
|
||||
export function addCollaborators(collaborators: string[]) {
|
||||
cy.get('[aria-label="Manage collaborators for this album"]').click()
|
||||
collaborators.forEach((collaborator: string) => {
|
||||
cy.get('[aria-label="Search for collaborators"').type(collaborator)
|
||||
cy.contains(collaborator).click()
|
||||
})
|
||||
cy.contains('Save').click()
|
||||
}
|
||||
|
||||
export function removeCollaborators(collaborators: string[]) {
|
||||
cy.get('[aria-label="Manage collaborators for this album"]').click()
|
||||
collaborators.forEach((collaborator: string) => {
|
||||
cy.get('.manage-collaborators')
|
||||
.within(() => {
|
||||
cy.contains(collaborator)
|
||||
.parentsUntil('ul')
|
||||
.get(`[aria-label="Remove ${collaborator} from the collaborators list"]`)
|
||||
.click()
|
||||
})
|
||||
})
|
||||
cy.contains('Save').click()
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 Louis Chmn <louis@chmn.me>
|
||||
*
|
||||
* @author Louis Chmn <louis@chmn.me>
|
||||
*
|
||||
* 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"
|
||||
|
||||
export function uploadTestMedia(user: User) {
|
||||
cy.exec('ls cypress/fixtures/media')
|
||||
.then((result) => {
|
||||
for (const fileName of result.stdout.split('\n')) {
|
||||
cy.uploadFile(user, `media/${fileName}`, 'image/png', `/${fileName}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function selectMedia(indexes: number[]) {
|
||||
indexes.forEach((index: number) => {
|
||||
cy.get('[data-test="media"]').eq(index)
|
||||
.find('a').focus()
|
||||
.parent().find('input').check({ force: true })
|
||||
})
|
||||
}
|
||||
|
||||
export function unselectMedia(indexes: number[]) {
|
||||
indexes.forEach((index: number) => {
|
||||
cy.get('[data-test="media"]').eq(index)
|
||||
.find('a').focus()
|
||||
.parent().find('input').uncheck({ force: true })
|
||||
})
|
||||
}
|
||||
|
||||
export function favoriteSelection() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Mark selection as favorite"]').click()
|
||||
}
|
||||
|
||||
export function unfavoriteSelection() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Remove selection from favorites"]').click()
|
||||
}
|
||||
|
||||
export function downloadSelection() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Download selected files"]').trigger('click')
|
||||
}
|
||||
|
||||
export function downloadAllFiles() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Download all files in album"]').trigger('click')
|
||||
}
|
||||
|
||||
export function deleteSelection() {
|
||||
cy.intercept({ method: 'DELETE' }).as('deleteRequests')
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Delete selection')
|
||||
.click()
|
||||
.wait('@deleteRequests')
|
||||
}
|
||||
|
||||
export function goToSharedAlbum(albumName: string) {
|
||||
cy.get('.app-navigation__list').contains('Collaborative albums').click()
|
||||
cy.get('ul.collections__list').contains(albumName).click()
|
||||
}
|
||||
|
||||
export function removeSharedAlbums() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Delete album').click()
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
export function goToSharedAlbum(albumName: string) {
|
||||
cy.get('.app-navigation__list').contains('Collaborative albums').click()
|
||||
cy.get('ul.collections__list').contains(albumName).click()
|
||||
}
|
||||
|
||||
export function removeSharedAlbums() {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Delete album').click()
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2022 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 { randHash } from '../utils'
|
||||
|
||||
const alice = `alice_${randHash()}`
|
||||
const bob = `bob_${randHash()}`
|
||||
const charlie = `charlie_${randHash()}`
|
||||
|
||||
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
describe('Manage shared albums', () => {
|
||||
before(() => {
|
||||
cy.visit('')
|
||||
cy.logout()
|
||||
|
||||
cy.nextcloudCreateUser(alice, 'password')
|
||||
cy.nextcloudCreateUser(bob, 'password')
|
||||
cy.nextcloudCreateUser(charlie, 'password')
|
||||
|
||||
cy.login(bob, 'password')
|
||||
cy.uploadTestMedia()
|
||||
cy.logout()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
cy.logout()
|
||||
cy.login(bob, 'password', '/apps/photos/sharedalbums')
|
||||
})
|
||||
|
||||
context('Adding and removing files in a shared album', () => {
|
||||
before(() => {
|
||||
cy.logout()
|
||||
cy.login(alice, 'password')
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
cy.createAnAlbumFromAlbums('shared_album_test1')
|
||||
cy.addCollaborators([bob])
|
||||
cy.logout()
|
||||
})
|
||||
|
||||
it('Add and remove a file to a shared album from a shared album', () => {
|
||||
cy.goToSharedAlbum('shared_album_test1')
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
cy.addFilesToAlbumFromAlbum('shared_album_test1', [0])
|
||||
cy.get('[data-test="media"]').should('have.length', 1)
|
||||
cy.selectMedia([0])
|
||||
cy.removeSelectionFromAlbum()
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
})
|
||||
|
||||
it('Add and remove multiple files to a shared album from a shared album', () => {
|
||||
cy.goToSharedAlbum('shared_album_test1')
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
cy.addFilesToAlbumFromAlbum('shared_album_test1', [1, 2])
|
||||
cy.get('[data-test="media"]').should('have.length', 2)
|
||||
cy.selectMedia([0, 1])
|
||||
cy.removeSelectionFromAlbum()
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
})
|
||||
})
|
||||
|
||||
context('Download files from a shared album', () => {
|
||||
before(() => {
|
||||
cy.logout()
|
||||
cy.login(alice, 'password')
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
cy.createAnAlbumFromAlbums('shared_album_test2')
|
||||
cy.addCollaborators([bob])
|
||||
cy.logout()
|
||||
|
||||
cy.login(bob, 'password')
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/sharedalbums`)
|
||||
cy.goToSharedAlbum('shared_album_test2')
|
||||
cy.addFilesToAlbumFromAlbum('shared_album_test2', [0, 1, 2])
|
||||
cy.logout()
|
||||
})
|
||||
|
||||
xit('Download a file from a shared album', () => {
|
||||
cy.goToSharedAlbum('shared_album_test2')
|
||||
cy.selectMedia([0])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([0])
|
||||
})
|
||||
|
||||
xit('Download multiple files from a shared album', () => {
|
||||
cy.goToSharedAlbum('shared_album_test2')
|
||||
cy.selectMedia([1, 2])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([1, 2])
|
||||
})
|
||||
|
||||
xit('Download all files from a shared album', () => {
|
||||
cy.goToSharedAlbum('shared_album_test2')
|
||||
cy.downloadAllFiles()
|
||||
})
|
||||
})
|
||||
|
||||
context('Delete a received shared album', () => {
|
||||
before(() => {
|
||||
cy.logout()
|
||||
cy.login(alice, 'password')
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
cy.createAnAlbumFromAlbums('shared_album_test3')
|
||||
cy.addCollaborators([bob])
|
||||
cy.logout()
|
||||
})
|
||||
|
||||
it('Remove shared album', () => {
|
||||
cy.goToSharedAlbum('shared_album_test3')
|
||||
cy.removeSharedAlbums()
|
||||
})
|
||||
})
|
||||
|
||||
context('Remove a collaborator from an album', () => {
|
||||
before(() => {
|
||||
cy.logout()
|
||||
cy.login(alice, 'password', '/apps/photos/albums')
|
||||
cy.createAnAlbumFromAlbums('shared_album_test4')
|
||||
cy.addCollaborators([bob])
|
||||
cy.logout()
|
||||
})
|
||||
|
||||
it('Remove collaborator from an album', () => {
|
||||
cy.get('ul.collections__list li')
|
||||
.should('contain', `shared_album_test4 (${alice})`)
|
||||
|
||||
cy.logout()
|
||||
cy.login(alice, 'password', '/apps/photos')
|
||||
cy.goToAlbum('shared_album_test4')
|
||||
cy.removeCollaborators([bob])
|
||||
cy.logout()
|
||||
|
||||
cy.login(bob, 'password', '/apps/photos/sharedalbums')
|
||||
cy.get('body')
|
||||
.should('not.contain', `shared_album_test4 (${alice})`)
|
||||
})
|
||||
})
|
||||
|
||||
context('Two shared albums with the same name', () => {
|
||||
before(() => {
|
||||
cy.logout()
|
||||
cy.login(alice, 'password')
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
cy.createAnAlbumFromAlbums('shared_album_test5')
|
||||
cy.addCollaborators([bob])
|
||||
cy.logout()
|
||||
|
||||
cy.login(charlie, 'password')
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
|
||||
cy.createAnAlbumFromAlbums('shared_album_test5')
|
||||
cy.addCollaborators([bob])
|
||||
cy.logout()
|
||||
})
|
||||
|
||||
|
||||
it('It should display two shared albums', () => {
|
||||
cy.get('ul.collections__list li')
|
||||
.contains(`shared_album_test5 (${alice})`)
|
||||
cy.get('ul.collections__list li')
|
||||
.contains(`shared_album_test5 (${charlie})`)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,194 @@
|
|||
|
||||
import { randHash } from '../utils'
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 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 { User } from '@nextcloud/cypress'
|
||||
import {
|
||||
addCollaborators,
|
||||
addFilesToAlbumFromAlbum,
|
||||
createAnAlbumFromAlbums,
|
||||
goToAlbum,
|
||||
removeCollaborators,
|
||||
removeSelectionFromAlbum,
|
||||
} from './albumsUtils'
|
||||
import {
|
||||
downloadAllFiles,
|
||||
downloadSelection,
|
||||
goToSharedAlbum,
|
||||
removeSharedAlbums,
|
||||
selectMedia,
|
||||
uploadTestMedia,
|
||||
} from './photosUtils'
|
||||
|
||||
import { randHash } from '../utils'
|
||||
|
||||
const alice = new User(`alice_${randHash()}`)
|
||||
const bob = new User(`bob_${randHash()}`)
|
||||
const charlie = new User(`charlie_${randHash()}`)
|
||||
|
||||
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
describe('Manage shared albums', () => {
|
||||
before(() => {
|
||||
cy.createUser(alice)
|
||||
cy.createUser(bob).then(() => {
|
||||
uploadTestMedia(bob)
|
||||
})
|
||||
cy.createUser(charlie)
|
||||
|
||||
})
|
||||
|
||||
context('Adding and removing files in a shared album', () => {
|
||||
before(() => {
|
||||
cy.login(alice)
|
||||
cy.visit('apps/photos/albums')
|
||||
createAnAlbumFromAlbums('shared_album_test1')
|
||||
addCollaborators([bob.userId])
|
||||
})
|
||||
|
||||
it('Add and remove a file to a shared album from a shared album', () => {
|
||||
cy.login(bob)
|
||||
cy.visit('apps/photos/albums')
|
||||
goToSharedAlbum('shared_album_test1')
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
addFilesToAlbumFromAlbum('shared_album_test1', [0])
|
||||
cy.get('[data-test="media"]').should('have.length', 1)
|
||||
selectMedia([0])
|
||||
removeSelectionFromAlbum()
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
})
|
||||
|
||||
it('Add and remove multiple files to a shared album from a shared album', () => {
|
||||
goToSharedAlbum('shared_album_test1')
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
addFilesToAlbumFromAlbum('shared_album_test1', [1, 2])
|
||||
cy.get('[data-test="media"]').should('have.length', 2)
|
||||
selectMedia([0, 1])
|
||||
removeSelectionFromAlbum()
|
||||
cy.get('[data-test="media"]').should('have.length', 0)
|
||||
})
|
||||
})
|
||||
|
||||
context('Download files from a shared album', () => {
|
||||
before(() => {
|
||||
cy.login(alice)
|
||||
cy.visit('apps/photos/albums')
|
||||
createAnAlbumFromAlbums('shared_album_test2')
|
||||
addCollaborators([bob.userId])
|
||||
|
||||
cy.login(bob)
|
||||
cy.visit('apps/photos/sharedalbums')
|
||||
goToSharedAlbum('shared_album_test2')
|
||||
addFilesToAlbumFromAlbum('shared_album_test2', [0, 1, 2])
|
||||
})
|
||||
|
||||
xit('Download a file from a shared album', () => {
|
||||
goToSharedAlbum('shared_album_test2')
|
||||
selectMedia([0])
|
||||
downloadSelection()
|
||||
selectMedia([0])
|
||||
})
|
||||
|
||||
xit('Download multiple files from a shared album', () => {
|
||||
goToSharedAlbum('shared_album_test2')
|
||||
selectMedia([1, 2])
|
||||
downloadSelection()
|
||||
selectMedia([1, 2])
|
||||
})
|
||||
|
||||
xit('Download all files from a shared album', () => {
|
||||
goToSharedAlbum('shared_album_test2')
|
||||
downloadAllFiles()
|
||||
})
|
||||
})
|
||||
|
||||
context('Delete a received shared album', () => {
|
||||
before(() => {
|
||||
cy.login(alice)
|
||||
cy.visit('apps/photos/albums')
|
||||
createAnAlbumFromAlbums('shared_album_test3')
|
||||
addCollaborators([bob.userId])
|
||||
})
|
||||
|
||||
it('Remove shared album', () => {
|
||||
cy.login(bob)
|
||||
cy.visit('apps/photos/albums')
|
||||
goToSharedAlbum('shared_album_test3')
|
||||
removeSharedAlbums()
|
||||
})
|
||||
})
|
||||
|
||||
context('Remove a collaborator from an album', () => {
|
||||
before(() => {
|
||||
cy.login(alice)
|
||||
cy.visit('/apps/photos/albums')
|
||||
createAnAlbumFromAlbums('shared_album_test4')
|
||||
addCollaborators([bob.userId])
|
||||
})
|
||||
|
||||
it('Remove collaborator from an album', () => {
|
||||
cy.login(bob)
|
||||
cy.visit('apps/photos/sharedalbums')
|
||||
cy.get('ul.collections__list li')
|
||||
.should('contain', `shared_album_test4 (${alice.userId})`)
|
||||
|
||||
cy.login(alice)
|
||||
cy.visit('/apps/photos')
|
||||
goToAlbum('shared_album_test4')
|
||||
removeCollaborators([bob.userId])
|
||||
|
||||
cy.login(bob)
|
||||
cy.visit('/apps/photos/sharedalbums')
|
||||
cy.get('body')
|
||||
.should('not.contain', `shared_album_test4 (${alice.userId})`)
|
||||
})
|
||||
})
|
||||
|
||||
context('Two shared albums with the same name', () => {
|
||||
before(() => {
|
||||
cy.login(alice)
|
||||
cy.visit('apps/photos/albums')
|
||||
createAnAlbumFromAlbums('shared_album_test5')
|
||||
addCollaborators([bob.userId])
|
||||
|
||||
cy.login(charlie)
|
||||
cy.visit('apps/photos/albums')
|
||||
createAnAlbumFromAlbums('shared_album_test5')
|
||||
addCollaborators([bob.userId])
|
||||
})
|
||||
|
||||
it('It should display two shared albums', () => {
|
||||
cy.login(bob)
|
||||
cy.visit('/apps/photos/sharedalbums')
|
||||
cy.get('ul.collections__list li')
|
||||
.contains(`shared_album_test5 (${alice.userId})`)
|
||||
cy.get('ul.collections__list li')
|
||||
.contains(`shared_album_test5 (${charlie.userId})`)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -19,93 +19,105 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import { randHash } from '../utils'
|
||||
const randUser = randHash()
|
||||
import {
|
||||
addFilesToAlbumFromTimeline,
|
||||
createAnAlbumFromTimeline,
|
||||
deleteAnAlbumFromAlbumContent,
|
||||
goToAlbum,
|
||||
} from './albumsUtils'
|
||||
|
||||
import {
|
||||
deleteSelection,
|
||||
downloadSelection,
|
||||
favoriteSelection,
|
||||
selectMedia,
|
||||
unfavoriteSelection,
|
||||
unselectMedia,
|
||||
uploadTestMedia,
|
||||
} from './photosUtils'
|
||||
|
||||
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false
|
||||
}
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
describe('View list of photos in the main timeline', () => {
|
||||
before(() => {
|
||||
cy.logout()
|
||||
cy.nextcloudCreateUser(randUser, 'password')
|
||||
before(() => {
|
||||
cy.createRandomUser()
|
||||
.then((user) => {
|
||||
uploadTestMedia(user)
|
||||
cy.login(user)
|
||||
cy.visit('/apps/photos')
|
||||
})
|
||||
})
|
||||
|
||||
cy.login(randUser, 'password')
|
||||
cy.uploadTestMedia()
|
||||
beforeEach(() => {
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos`)
|
||||
})
|
||||
|
||||
// wait a bit for things to be settled
|
||||
cy.wait(1000)
|
||||
})
|
||||
it('Favorite a file from a timeline', () => {
|
||||
selectMedia([0])
|
||||
favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(0).find('[aria-label="The file is in the favorites"]')
|
||||
unfavoriteSelection()
|
||||
unselectMedia([0])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos`)
|
||||
})
|
||||
it('Favorite multiple files from a timeline', () => {
|
||||
selectMedia([1, 2])
|
||||
favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(1).find('[aria-label="The file is in the favorites"]')
|
||||
cy.get('[data-test="media"]').eq(2).find('[aria-label="The file is in the favorites"]')
|
||||
unfavoriteSelection()
|
||||
unselectMedia([1, 2])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
|
||||
it('Favorite a file from a timeline', () => {
|
||||
cy.selectMedia([0])
|
||||
cy.favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(0).find('[aria-label="The file is in the favorites"]')
|
||||
cy.unfavoriteSelection()
|
||||
cy.unselectMedia([0])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
it('Download a file from a timeline', () => {
|
||||
selectMedia([0])
|
||||
downloadSelection()
|
||||
unselectMedia([0])
|
||||
})
|
||||
|
||||
it('Favorite multiple files from a timeline', () => {
|
||||
cy.selectMedia([1, 2])
|
||||
cy.favoriteSelection()
|
||||
cy.get('[data-test="media"]').eq(1).find('[aria-label="The file is in the favorites"]')
|
||||
cy.get('[data-test="media"]').eq(2).find('[aria-label="The file is in the favorites"]')
|
||||
cy.unfavoriteSelection()
|
||||
cy.unselectMedia([1, 2])
|
||||
cy.get('[aria-label="The file is in the favorites"]').should('not.exist')
|
||||
})
|
||||
it('Download multiple files from a timeline', () => {
|
||||
selectMedia([1, 2])
|
||||
downloadSelection()
|
||||
unselectMedia([1, 2])
|
||||
})
|
||||
|
||||
it('Download a file from a timeline', () => {
|
||||
cy.selectMedia([0])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([0])
|
||||
})
|
||||
it('Add file to an album from a timeline', () => {
|
||||
createAnAlbumFromTimeline('timeline_test_single')
|
||||
selectMedia([0])
|
||||
addFilesToAlbumFromTimeline('timeline_test_single')
|
||||
goToAlbum('timeline_test_single')
|
||||
cy.get('[data-test="media"]').should('have.length', 1)
|
||||
deleteAnAlbumFromAlbumContent()
|
||||
})
|
||||
|
||||
it('Download multiple files from a timeline', () => {
|
||||
cy.selectMedia([1, 2])
|
||||
cy.downloadSelection()
|
||||
cy.unselectMedia([1, 2])
|
||||
})
|
||||
it('Add multiple files to an album from a timeline', () => {
|
||||
createAnAlbumFromTimeline('timeline_test_multiple')
|
||||
selectMedia([1, 2])
|
||||
addFilesToAlbumFromTimeline('timeline_test_multiple')
|
||||
goToAlbum('timeline_test_multiple')
|
||||
cy.get('[data-test="media"]').should('have.length', 2)
|
||||
deleteAnAlbumFromAlbumContent()
|
||||
})
|
||||
|
||||
it('Add file to an album from a timeline', () => {
|
||||
cy.createAnAlbumFromTimeline('timeline_test_single')
|
||||
cy.selectMedia([0])
|
||||
cy.addFilesToAlbumFromTimeline('timeline_test_single')
|
||||
cy.goToAlbum('timeline_test_single')
|
||||
cy.get('[data-test="media"]').should('have.length', 1)
|
||||
cy.deleteAnAlbumFromAlbumContent()
|
||||
})
|
||||
it('Delete a file from photos', () => {
|
||||
cy.get('[data-test="media"]').should('have.length', 5)
|
||||
selectMedia([0])
|
||||
deleteSelection()
|
||||
cy.get('[data-test="media"]').should('have.length', 4)
|
||||
})
|
||||
|
||||
it('Add multiple files to an album from a timeline', () => {
|
||||
cy.createAnAlbumFromTimeline('timeline_test_multiple')
|
||||
cy.selectMedia([1, 2])
|
||||
cy.addFilesToAlbumFromTimeline('timeline_test_multiple')
|
||||
cy.goToAlbum('timeline_test_multiple')
|
||||
cy.get('[data-test="media"]').should('have.length', 2)
|
||||
cy.deleteAnAlbumFromAlbumContent()
|
||||
})
|
||||
|
||||
it('Delete a file from photos', () => {
|
||||
cy.get('[data-test="media"]').should('have.length', 5)
|
||||
cy.selectMedia([0])
|
||||
cy.deleteSelection()
|
||||
cy.get('[data-test="media"]').should('have.length', 4)
|
||||
})
|
||||
|
||||
it('Delete multiple files from photos', () => {
|
||||
cy.get('[data-test="media"]').should('have.length', 4)
|
||||
cy.selectMedia([1, 2])
|
||||
cy.deleteSelection()
|
||||
cy.get('[data-test="media"]').should('have.length', 2)
|
||||
})
|
||||
})
|
||||
it('Delete multiple files from photos', () => {
|
||||
cy.get('[data-test="media"]').should('have.length', 4)
|
||||
selectMedia([1, 2])
|
||||
deleteSelection()
|
||||
cy.get('[data-test="media"]').should('have.length', 2)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "APP_NAME: $APP_NAME"
|
||||
echo "BRANCH: $BRANCH"
|
||||
|
||||
chown -R www-data:www-data /var/www/html/data
|
||||
|
||||
su www-data -c "
|
||||
php occ config:system:set force_language --value en
|
||||
php occ config:system:set enforce_theme --value light
|
||||
php occ app:enable $APP_NAME
|
||||
php occ app:list
|
||||
"
|
||||
|
||||
cd apps
|
||||
git clone --depth 1 https://github.com/nextcloud/viewer.git
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# RUN THIS SCRIPT FROM THE ROOT FOLDER OF YOUR APP
|
||||
APP_NAME=${PWD##*/}
|
||||
CYPRESS_baseUrl=http://127.0.0.1:8082/index.php
|
||||
|
||||
if [[ $APP_NAME == "cypress" ]]
|
||||
then
|
||||
echo "Please run this app from your app root folder."
|
||||
else
|
||||
echo "Launching docker server for the $APP_NAME app"
|
||||
cd cypress
|
||||
docker-compose pull
|
||||
docker-compose up -d --force-recreate
|
||||
npm run wait-on $CYPRESS_baseUrl
|
||||
echo "Nextcloud successfully installed"
|
||||
docker-compose exec --env APP_NAME=$APP_NAME -T nextcloud bash /initserver.sh
|
||||
echo "Nextcloud successfully configured"
|
||||
fi
|
|
@ -1,12 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# RUN THIS SCRIPT FROM THE ROOT FOLDER OF YOUR APP
|
||||
appname=${PWD##*/}
|
||||
|
||||
if [[ $appname == "cypress" ]]
|
||||
then
|
||||
echo "Please run this app from your app root folder."
|
||||
else
|
||||
echo "Killing server for the $appname app"
|
||||
cd cypress
|
||||
docker-compose stop
|
||||
fi
|
|
@ -1,247 +0,0 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
const url = Cypress.config('baseUrl').replace(/\/index.php\/?$/g, '')
|
||||
Cypress.env('baseUrl', url)
|
||||
|
||||
Cypress.Commands.add('login', (user, password, route = '/apps/files') => {
|
||||
Cypress.Cookies.defaults({
|
||||
preserve: /^(oc|nc)/,
|
||||
})
|
||||
cy.visit(route)
|
||||
cy.get('input[name=user]').type(user)
|
||||
cy.get('input[name=password]').type(password)
|
||||
cy.get('form[name=login] [type=submit]').click()
|
||||
cy.url().should('include', route)
|
||||
})
|
||||
|
||||
Cypress.Commands.add('logout', () => {
|
||||
cy.getCookies()
|
||||
.then(cookies => {
|
||||
if (cookies.length === 0) {
|
||||
cy.log('Not logged, skipping logout...')
|
||||
return
|
||||
}
|
||||
|
||||
return cy.get("body")
|
||||
.then($body => {
|
||||
const $settingsButton = $body.find('#settings #expand')
|
||||
if ($settingsButton.length === 0) {
|
||||
cy.log("Not logged in.")
|
||||
return
|
||||
}
|
||||
|
||||
$settingsButton.click()
|
||||
cy.contains('Log out').click()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('nextcloudCreateUser', (user, password) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env('baseUrl')}/ocs/v1.php/cloud/users?format=json`,
|
||||
form: true,
|
||||
body: {
|
||||
userid: user,
|
||||
password,
|
||||
},
|
||||
auth: { user: 'admin', pass: 'admin' },
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'OCS-ApiRequest': 'true',
|
||||
Authorization: `Basic ${Buffer.from('admin:admin').toString('base64')}`,
|
||||
},
|
||||
})
|
||||
cy.clearCookies()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('uploadTestMedia', () => {
|
||||
cy.exec('ls cypress/fixtures/media')
|
||||
.then((result) => {
|
||||
for (const fileName of result.stdout.split('\n')) {
|
||||
cy.uploadFile(`media/${fileName}`, 'image/png', '', fileName)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* cy.uploadedFile - uploads a file from the fixtures folder
|
||||
*
|
||||
* @param {string} fixtureFileName
|
||||
* @param {string} mimeType eg. image/png
|
||||
* @param {string} path to the folder in which this file should be uploaded
|
||||
* @param {string} uploadedFileName alternative name to give the file while uploading
|
||||
*/
|
||||
Cypress.Commands.add('uploadFile', (fixtureFileName, mimeType, path = '', uploadedFileName = null) => {
|
||||
if (uploadedFileName === null) {
|
||||
uploadedFileName = fixtureFileName;
|
||||
}
|
||||
|
||||
let file = null
|
||||
|
||||
return cy.fixture(fixtureFileName, 'base64')
|
||||
.then(fileBase64 => {
|
||||
// convert the logo base64 string to a blob
|
||||
const blob = Cypress.Blob.base64StringToBlob(fileBase64, mimeType)
|
||||
file = new File([blob], uploadedFileName, { type: mimeType })
|
||||
return cy.window()
|
||||
})
|
||||
.then(window => {
|
||||
const encodedPath = path.split("/")
|
||||
.map(encodeURIComponent)
|
||||
.join("/")
|
||||
|
||||
const url = `${Cypress.env('baseUrl')}/remote.php/webdav${encodedPath}/${encodeURIComponent(uploadedFileName)}`
|
||||
return cy.request({
|
||||
method: 'PUT',
|
||||
url,
|
||||
body: file,
|
||||
encoding: 'binary',
|
||||
headers: {
|
||||
'Content-Type': mimeType,
|
||||
requesttoken: window.OC.requestToken,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('selectMedia', (indexes) => {
|
||||
indexes.forEach(index => {
|
||||
cy.get('[data-test="media"]').eq(index)
|
||||
.find('a').focus()
|
||||
.parent().find('input').check({ force: true })
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('unselectMedia', indexes => {
|
||||
indexes.forEach(index => {
|
||||
cy.get('[data-test="media"]').eq(index)
|
||||
.find('a').focus()
|
||||
.parent().find('input').uncheck({ force: true })
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('favoriteSelection', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Mark selection as favorite"]').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('unfavoriteSelection', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Remove selection from favorites"]').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('downloadSelection', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Download selected files"]').trigger('click')
|
||||
})
|
||||
|
||||
Cypress.Commands.add('downloadAllFiles', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.get('[aria-label="Download all files in album"]').trigger('click')
|
||||
})
|
||||
|
||||
Cypress.Commands.add('createAnAlbumFromTimeline', albumName => {
|
||||
cy.contains('Add').click()
|
||||
cy.contains('Create new album').click()
|
||||
cy.get('form [name="name"]').type(albumName)
|
||||
cy.contains('Create album').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('createAnAlbumFromAlbums', albumName => {
|
||||
cy.contains('New album').click()
|
||||
cy.get('form [name="name"]').type(albumName)
|
||||
cy.contains('Create album').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('deleteAnAlbumFromAlbumContent', albumName => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains('Delete album').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('addFilesToAlbumFromTimeline', albumName => {
|
||||
cy.contains('Add to album').click()
|
||||
cy.get('.album-picker ul').contains(albumName).click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('addFilesToAlbumFromAlbum', (albumName, itemsIndex) => {
|
||||
cy.get('[aria-label="Add photos to this album"]').click()
|
||||
cy.get('.file-picker__file-list').within(() => {
|
||||
cy.selectMedia(itemsIndex)
|
||||
})
|
||||
cy.contains(`Add to ${albumName}`).click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('deleteSelection', () => {
|
||||
cy.intercept({ method: 'DELETE' }).as("deleteRequests");
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains("Delete selection")
|
||||
.click()
|
||||
.wait('@deleteRequests')
|
||||
})
|
||||
|
||||
Cypress.Commands.add('removeSelectionFromAlbum', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains("Remove selection from album").click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('goToAlbum', albumName => {
|
||||
cy.get('.app-navigation__list').contains('Albums').click()
|
||||
cy.get('ul.collections__list').contains(albumName).click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('goToSharedAlbum', albumName => {
|
||||
cy.get('.app-navigation__list').contains('Collaborative albums').click()
|
||||
cy.get('ul.collections__list').contains(albumName).click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('addCollaborators', collaborators => {
|
||||
cy.get('[aria-label="Manage collaborators for this album"]').click()
|
||||
collaborators.forEach((collaborator) => {
|
||||
cy.get('[aria-label="Search for collaborators"').type(collaborator)
|
||||
cy.contains(collaborator).click()
|
||||
})
|
||||
cy.contains('Save').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('removeCollaborators', collaborators => {
|
||||
cy.get('[aria-label="Manage collaborators for this album"]').click()
|
||||
collaborators.forEach((collaborator) => {
|
||||
cy.get('.manage-collaborators')
|
||||
.within(() => {
|
||||
cy.contains(collaborator)
|
||||
.parentsUntil('ul')
|
||||
.get(`[aria-label="Remove ${collaborator} from the collaborators list"]`)
|
||||
.click()
|
||||
})
|
||||
})
|
||||
cy.contains('Save').click()
|
||||
})
|
||||
|
||||
Cypress.Commands.add('removeSharedAlbums', () => {
|
||||
cy.get('[aria-label="Open actions menu"]').click()
|
||||
cy.contains("Delete album").click()
|
||||
})
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
/* eslint-disable n/no-unpublished-import */
|
||||
import axios from '@nextcloud/axios'
|
||||
import { addCommands, User } from '@nextcloud/cypress'
|
||||
import { basename } from 'path'
|
||||
|
||||
// Add custom commands
|
||||
import 'cypress-wait-until'
|
||||
addCommands()
|
||||
|
||||
// Register this file's custom commands types
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable<Subject = any> {
|
||||
/**
|
||||
* Upload a file from the fixtures folder to a given user storage.
|
||||
* **Warning**: Using this function will reset the previous session
|
||||
*/
|
||||
uploadFile(user: User, fixture?: string, mimeType?: string, target?: string): Cypress.Chainable<void>,
|
||||
|
||||
/**
|
||||
* Upload a raw content to a given user storage.
|
||||
* **Warning**: Using this function will reset the previous session
|
||||
*/
|
||||
uploadContent(user: User, content: Blob, mimeType: string, target: string): Cypress.Chainable<void>,
|
||||
|
||||
/**
|
||||
* Run an occ command in the docker container.
|
||||
*/
|
||||
runOccCommand(command: string): Cypress.Chainable<void>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const url = (Cypress.config('baseUrl') || '').replace(/\/index.php\/?$/g, '')
|
||||
Cypress.env('baseUrl', url)
|
||||
|
||||
/**
|
||||
* cy.uploadedFile - uploads a file from the fixtures folder
|
||||
* TODO: standardise in @nextcloud/cypress
|
||||
*
|
||||
* @param {User} user the owner of the file, e.g. admin
|
||||
* @param {string} fixture the fixture file name, e.g. image1.jpg
|
||||
* @param {string} mimeType e.g. image/png
|
||||
* @param {string} [target] the target of the file relative to the user root
|
||||
*/
|
||||
Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'image/jpeg', target = `/${fixture}`) => {
|
||||
// get fixture
|
||||
return cy.fixture(fixture, 'base64').then(async file => {
|
||||
// convert the base64 string to a blob
|
||||
const blob = Cypress.Blob.base64StringToBlob(file, mimeType)
|
||||
cy.uploadContent(user, blob, mimeType, target)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* cy.uploadedContent - uploads a raw content
|
||||
* TODO: standardise in @nextcloud/cypress
|
||||
*
|
||||
* @param {User} user the owner of the file, e.g. admin
|
||||
* @param {Blob} blob the content to upload
|
||||
* @param {string} mimeType e.g. image/png
|
||||
* @param {string} target the target of the file relative to the user root
|
||||
*/
|
||||
Cypress.Commands.add('uploadContent', (user, blob, mimeType, target) => {
|
||||
cy.clearCookies()
|
||||
.then(async () => {
|
||||
const fileName = basename(target)
|
||||
|
||||
// Process paths
|
||||
const rootPath = `${Cypress.env('baseUrl')}/remote.php/dav/files/${encodeURIComponent(user.userId)}`
|
||||
const filePath = target.split('/').map(encodeURIComponent).join('/')
|
||||
try {
|
||||
const file = new File([blob], fileName, { type: mimeType })
|
||||
await axios({
|
||||
url: `${rootPath}${filePath}`,
|
||||
method: 'PUT',
|
||||
data: file,
|
||||
headers: {
|
||||
'Content-Type': mimeType,
|
||||
},
|
||||
auth: {
|
||||
username: user.userId,
|
||||
password: user.password,
|
||||
},
|
||||
}).then(response => {
|
||||
cy.log(`Uploaded content as ${fileName}`, response)
|
||||
})
|
||||
} catch (error) {
|
||||
cy.log('error', error)
|
||||
throw new Error(`Unable to process fixture`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('runOccCommand', (command: string) => {
|
||||
cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server php ./occ ${command}`)
|
||||
})
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-cy-root></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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 { mount } from 'cypress/vue2'
|
||||
|
||||
// Augment the Cypress namespace to include type definitions for
|
||||
// your custom command.
|
||||
// Alternatively, can be defined in cypress/support/component.d.ts
|
||||
// with a <reference path="./component" /> at the top of your spec.
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
mount: typeof mount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example use:
|
||||
// cy.mount(MyComponent)
|
||||
Cypress.Commands.add('mount', (component, optionsOrProps) => {
|
||||
let instance = null
|
||||
const oldMounted = component?.mounted || false
|
||||
|
||||
// Override the mounted method to expose
|
||||
// the component instance to cypress
|
||||
component.mounted = function() {
|
||||
// eslint-disable-next-line
|
||||
instance = this
|
||||
if (oldMounted) {
|
||||
oldMounted()
|
||||
}
|
||||
}
|
||||
|
||||
// Expose the component with cy.get('@component')
|
||||
return mount(component, optionsOrProps).then(() => {
|
||||
return cy.wrap(instance).as('component')
|
||||
})
|
||||
})
|
|
@ -1,17 +0,0 @@
|
|||
// ***********************************************************
|
||||
// This example support/e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands.js'
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @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 './commands'
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["./**/*.ts"],
|
||||
}
|
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
|
@ -30,11 +30,11 @@
|
|||
"lint:fix": "eslint --ext .js,.vue src --fix",
|
||||
"stylelint": "stylelint src",
|
||||
"stylelint:fix": "stylelint src --fix",
|
||||
"e2e": "./cypress/start.sh; cypress run; ./cypress/stop.sh",
|
||||
"e2e:gui": "./cypress/start.sh; cypress open; ./cypress/stop.sh",
|
||||
"precypress:update-snapshots": "TESTING=true npm run dev",
|
||||
"cypress:update-snapshots": "TESTING=true cypress run --env type=base --spec cypress/e2e/visual-regression.cy.js --config screenshotsFolder=cypress/snapshots/base",
|
||||
"wait-on": "wait-on -i 500 -t 300000"
|
||||
"cypress": "npm run cypress:component && npm run cypress:e2e",
|
||||
"cypress:component": "cypress run --component",
|
||||
"cypress:e2e": "cypress run --e2e",
|
||||
"cypress:gui": "cypress open",
|
||||
"precypress:update-snapshots": "TESTING=true npm run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@essentials/request-timeout": "^1.3.0",
|
||||
|
@ -53,6 +53,7 @@
|
|||
"@nextcloud/upload": "^1.0.0-beta.8",
|
||||
"@nextcloud/vue": "^7.3.0",
|
||||
"camelcase": "^7.0.0",
|
||||
"cypress": "^12.5.0",
|
||||
"debounce": "^1.2.1",
|
||||
"he": "^1.2.0",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
|
@ -79,20 +80,29 @@
|
|||
"@cypress/browserify-preprocessor": "^3.0.2",
|
||||
"@nextcloud/babel-config": "^1.0.0",
|
||||
"@nextcloud/browserslist-config": "^2.3.0",
|
||||
"@nextcloud/cypress": "^1.0.0-beta.2",
|
||||
"@nextcloud/eslint-config": "^8.0.0",
|
||||
"@nextcloud/stylelint-config": "^2.3.0",
|
||||
"@nextcloud/webpack-vue-config": "^5.4.0",
|
||||
"@types/dockerode": "^3.3.14",
|
||||
"@vue/test-utils": "^1.3.3",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader-exclude-node-modules-except": "^1.2.1",
|
||||
"cypress": "^11.2.0",
|
||||
"cypress-visual-regression": "^2.1.0",
|
||||
"cypress-wait-until": "^1.7.2",
|
||||
"dockerode": "^3.3.4",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"jest": "^29.4.1",
|
||||
"module-replace-webpack-plugin": "0.0.12",
|
||||
"postcss": "^8.4.19",
|
||||
"postcss-loader": "^7.0.2",
|
||||
"raw-loader": "^4.0.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.5.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vue-jest": "^3.0.7",
|
||||
"wait-on": "^6.0.1",
|
||||
"workbox-webpack-plugin": "^6.5.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.json",
|
||||
"include": ["./src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["cypress", "node", "dockerode"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "node",
|
||||
"target": "ESNext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"ts-node": {
|
||||
// these options are overrides used only by ts-node
|
||||
// same as our --compilerOptions flag and our TS_NODE_COMPILER_OPTIONS environment variable
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue