From 1231057afc59818e93204d333fa7605a40074f05 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Tue, 27 Aug 2024 15:27:40 +0200 Subject: [PATCH] chore: fix cypress config Signed-off-by: skjnldsv --- .github/workflows/cypress.yml | 31 ++--- cypress.config.ts | 30 +---- cypress/dockerNode.ts | 218 ---------------------------------- cypress/support/commands.ts | 2 +- package-lock.json | 44 +------ package.json | 11 +- 6 files changed, 22 insertions(+), 314 deletions(-) delete mode 100644 cypress/dockerNode.ts diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 0ea49ec60..7904e8d85 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -18,11 +18,8 @@ env: # Adjust APP_NAME if your repository name is different APP_NAME: ${{ github.event.repository.name }} - # This represents the server branch to checkout. - # Usually it's the base branch of the PR, but for pushes it's the branch itself. - # e.g. 'main', 'stable27' or 'feature/my-feature' - # n.b. server will use head_ref, as we want to test the PR branch. - BRANCH: ${{ github.base_ref || github.ref_name }} + # Server requires head_ref instead of base_ref, as we want to test the PR branch + BRANCH: ${{ github.head_ref || github.ref_name }} jobs: init: @@ -32,6 +29,8 @@ jobs: npmVersion: ${{ steps.versions.outputs.npmVersion }} env: + # We'll install cypress in the cypress job + CYPRESS_INSTALL_BINARY: 0 PUPPETEER_SKIP_DOWNLOAD: true steps: @@ -89,9 +88,9 @@ jobs: matrix: # Run multiple copies of the current job in parallel # Please increase the number or runners as your tests suite grows (0 based index for e2e tests) - containers: [0, 1, 2] + containers: ['0', '1', '2'] # Hack as strategy.job-total includes the component and GitHub does not allow math expressions - # Always aling this number with the total of e2e runners (max. index + 1) + # Always align this number with the total of e2e runners (max. index + 1) total-containers: [3] name: runner ${{ matrix.containers }} @@ -112,17 +111,19 @@ jobs: - name: Set up npm ${{ needs.init.outputs.npmVersion }} run: npm i -g 'npm@${{ needs.init.outputs.npmVersion }}' + - name: Install cypress + run: ./node_modules/cypress/bin/cypress install + - name: Run ${{ matrix.containers == 'component' && 'component' || 'E2E' }} cypress tests uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2 with: - record: ${{ secrets.CYPRESS_RECORD_KEY && true }} - parallel: ${{ secrets.CYPRESS_RECORD_KEY && true }} - # cypress run type + # We already installed the dependencies in the init job + install: false component: ${{ matrix.containers == 'component' }} - group: ${{ secrets.CYPRESS_RECORD_KEY && env.CYPRESS_GROUP }} + group: ${{ matrix.use-cypress-cloud && matrix.containers == 'component' && 'Run component' || matrix.use-cypress-cloud && 'Run E2E' || '' }} # cypress env - ci-build-id: ${{ secrets.CYPRESS_RECORD_KEY && env.CYPRESS_BUILD_ID }} - tag: ${{ secrets.CYPRESS_RECORD_KEY && github.event_name }} + ci-build-id: ${{ matrix.use-cypress-cloud && format('{0}-{1}', github.sha, github.run_number) || '' }} + tag: ${{ matrix.use-cypress-cloud && github.event_name || '' }} env: # Needs to be prefixed with CYPRESS_ CYPRESS_BRANCH: ${{ env.BRANCH }} @@ -136,7 +137,7 @@ jobs: SPLIT_INDEX: ${{ matrix.containers == 'component' && 0 || matrix.containers }} - name: Upload snapshots - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 if: always() with: name: snapshots_${{ matrix.containers }} @@ -147,7 +148,7 @@ jobs: run: docker logs nextcloud-cypress-tests-${{ env.APP_NAME }} > nextcloud.log - name: Upload NC logs - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 if: failure() && matrix.containers != 'component' with: name: nc_logs_${{ matrix.containers }} diff --git a/cypress.config.ts b/cypress.config.ts index 5c6693293..779214604 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,5 +1,4 @@ import { configureNextcloud, startNextcloud, stopNextcloud, waitOnNextcloud } from '@nextcloud/cypress/docker' -import { configureVisualRegression } from 'cypress-visual-regression/dist/plugin' import { defineConfig } from 'cypress' import cypressSplit from 'cypress-split' @@ -27,14 +26,6 @@ export default defineConfig({ // https://github.com/cypress-io/cypress/issues/871 scrollBehavior: 'center', - // Visual regression testing - env: { - failSilently: false, - visualRegressionType: 'regression', - }, - screenshotsFolder: 'cypress/snapshots/actual', - trashAssetsBeforeRuns: true, - e2e: { // Disable session isolation testIsolation: false, @@ -43,25 +34,6 @@ export default defineConfig({ // You may want to clean this up later by importing these. async setupNodeEvents(on, config) { cypressSplit(on, config) - configureVisualRegression(on) - - // 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', () => { @@ -76,7 +48,7 @@ export default defineConfig({ // Setting container's IP as base Url config.baseUrl = `http://${ip}/index.php` await waitOnNextcloud(ip) - await configureNextcloud([]) // pass empty array as WE are already the viewer + await configureNextcloud(['groupfolders']) // pass empty array as WE are already the viewer return config }, }, diff --git a/cypress/dockerNode.ts b/cypress/dockerNode.ts deleted file mode 100644 index 9effe4b56..000000000 --- a/cypress/dockerNode.ts +++ /dev/null @@ -1,218 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -/* eslint-disable no-console */ -import Docker from 'dockerode' -import path from 'path' -import waitOn from 'wait-on' - -import pkg from '../package.json' - -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' - -export const docker = new Docker() - -/** - * Start the testing container - * - * @param branch the current git branch - */ -export const startNextcloud = async function(branch = 'master'): Promise { - try { - // Pulling images - console.log('Pulling images... ā³') - await new Promise((resolve, reject) => docker.pull(SERVER_IMAGE, (_err, stream: NodeJS.ReadableStream) => { - const onFinished = function(err: Error | null) { - if (!err) { - return resolve(true) - } - reject(err) - } - // https://github.com/apocas/dockerode/issues/357 - docker.modem.followProgress(stream, onFinished) - })) - console.log('ā””ā”€ Done') - - // Getting latest image - console.log('\nChecking running containers... šŸ”') - const localImage = await docker.listImages({ filters: `{"reference": ["${SERVER_IMAGE}"]}` }) - - // Remove old container if exists and not initialized by us - try { - const oldContainer = docker.getContainer(CONTAINER_NAME) - const oldContainerData = await oldContainer.inspect() - if (oldContainerData.State.Running) { - console.log('ā”œā”€ Existing running container found') - if (localImage[0].Id !== oldContainerData.Image) { - console.log('ā””ā”€ But running container is outdated, replacing...') - } else { - // Get container's IP - console.log('ā”œā”€ Reusing that container') - const ip = await getContainerIP(oldContainer) - return ip - } - } else { - console.log('ā””ā”€ None found!') - } - // Forcing any remnants to be removed just in case - await oldContainer.remove({ force: true }) - } 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: [`${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 - * - * @param branch - */ -export const configureNextcloud = async function(branch = '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) - - // Enable the app and give status - await runExec(container, ['php', 'occ', 'app:enable', '--force', 'viewer'], true) - await runExec(container, ['php', 'occ', 'app:enable', 'groupfolders', '--force'], true) - await runExec(container, ['php', 'occ', 'app:enable', 'files_trashbin', '--force'], true) - // await runExec(container, ['php', 'occ', 'app:list'], true) - - console.log('ā””ā”€ Nextcloud is now ready to use šŸŽ‰') -} - -/** - * Force stop the testing nextcloud 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 address - * - * @param container the container to get the ip from - */ -export const getContainerIP = async function( - container: Docker.Container = docker.getContainer(CONTAINER_NAME), -): Promise { - 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 - * - * @param {string} ip the ip to wait for - * @see 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)) -} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 65cbaf684..19c2f6840 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -129,5 +129,5 @@ Cypress.Commands.add('uploadContent', (user, blob, mimeType, target) => { Cypress.Commands.add('runOccCommand', (command: string, options?: Partial) => { const env = Object.entries(options?.env ?? {}).map(([name, value]) => `-e '${name}=${value}'`).join(' ') - return cy.exec(`docker exec --user www-data ${env} nextcloud-cypress-tests-groupfolders php ./occ ${command}`, options) + return cy.exec(`docker exec --user www-data ${env} nextcloud-cypress-tests_groupfolders php ./occ ${command}`, options) }) diff --git a/package-lock.json b/package-lock.json index ab8eefc69..036f5337b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "@nextcloud/typings": "^1.9.1", "@nextcloud/webpack-vue-config": "^5.5.1", "@types/bootstrap": "^5.2.10", - "@types/dockerode": "^3.3.30", "@types/jest": "^29.5.12", "@types/jquery": "^3.5.29", "@types/react": "^17.0.43", @@ -55,7 +54,6 @@ "cypress-split": "^1.24.0", "cypress-visual-regression": "^5.2.0", "cypress-wait-until": "^3.0.2", - "dockerode": "^4.0.2", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-react": "^7.34.1", "jest": "^29.4.1", @@ -65,8 +63,7 @@ "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "tslib": "^2.7.0", - "typescript": "^5.5.4", - "wait-on": "^7.0.1" + "typescript": "^5.5.4" }, "engines": { "node": "^20.0.0", @@ -4895,27 +4892,6 @@ "@types/ms": "*" } }, - "node_modules/@types/docker-modem": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", - "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/ssh2": "*" - } - }, - "node_modules/@types/dockerode": { - "version": "3.3.30", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.30.tgz", - "integrity": "sha512-MuxQQ7yQ+VzLbrIV2D8K2YqOYBd5Mz4yGbauEipFcn894bLnGwewNKXGKLlb7wpXTG4dn+13lk51qPXmKJ2+YA==", - "dev": true, - "dependencies": { - "@types/docker-modem": "*", - "@types/node": "*", - "@types/ssh2": "*" - } - }, "node_modules/@types/dompurify": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", @@ -5255,24 +5231,6 @@ "@types/node": "*" } }, - "node_modules/@types/ssh2": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.11.18.tgz", - "integrity": "sha512-7eH4ppQMFlzvn//zhwD54MWaITR1aSc1oFBye9vb76GZ2Y9PSFYdwVIwOlxRXWs5+1hifntXyt+8a6SUbOD7Hg==", - "dev": true, - "dependencies": { - "@types/node": "^18.11.18" - } - }, - "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.6.tgz", - "integrity": "sha512-X36s5CXMrrJOs2lQCdDF68apW4Rfx9ixYMawlepwmE4Anezv/AV2LSpKD1Ub8DAc+urp5bk0BGZ6NtmBitfnsg==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", diff --git a/package.json b/package.json index a1860c356..ab1bbc9d7 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,9 @@ "lint:fix": "eslint --ext .js,.vue src --fix", "stylelint": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue", "stylelint:fix": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue --fix", - "cypress": "npm run cypress:component && npm run cypress:e2e", - "cypress:component": "cypress run --component", + "cypress": "npm run cypress:e2e", "cypress:e2e": "cypress run --e2e", - "cypress:gui": "cypress open", - "precypress:update-snapshots": "TESTING=true npm run dev" + "cypress:gui": "cypress open" }, "engines": { "node": "^20.0.0", @@ -28,7 +26,6 @@ "@nextcloud/typings": "^1.9.1", "@nextcloud/webpack-vue-config": "^5.5.1", "@types/bootstrap": "^5.2.10", - "@types/dockerode": "^3.3.30", "@types/jest": "^29.5.12", "@types/jquery": "^3.5.29", "@types/react": "^17.0.43", @@ -43,7 +40,6 @@ "cypress-split": "^1.24.0", "cypress-visual-regression": "^5.2.0", "cypress-wait-until": "^3.0.2", - "dockerode": "^4.0.2", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-react": "^7.34.1", "jest": "^29.4.1", @@ -53,8 +49,7 @@ "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "tslib": "^2.7.0", - "typescript": "^5.5.4", - "wait-on": "^7.0.1" + "typescript": "^5.5.4" }, "dependencies": { "@nextcloud/auth": "^2.4.0",