Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable25] chore: update cypress.yml workflow from template #1767

Merged
merged 3 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ on:
pull_request:
push:
branches:
- main
- master
- stable*

env:
APP_NAME: viewer
BRANCH: ${{ github.base_ref }}
CYPRESS_baseUrl: http://127.0.0.1:8082/index.php
TESTING: true
# 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 }}

jobs:
init:
Expand All @@ -24,6 +29,16 @@ jobs:
- name: Checkout app
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2

- name: Check composer.json
id: check_composer
uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b # v2
with:
files: "composer.json"

- name: Install composer dependencies
if: steps.check_composer.outputs.files_exists == 'true'
run: composer install --no-dev

- name: Read package.json node and npm engines version
uses: skjnldsv/read-package-engines-version-actions@0ce2ed60f6df073a62a77c0a4958dd0fc68e32e7 # v2.1
id: versions
Expand All @@ -34,19 +49,18 @@ jobs:
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
cache: 'npm'
node-version: ${{ steps.versions.outputs.nodeVersion }}

- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"

- name: Install dependencies & build app
- name: Install node dependencies & build app
run: |
npm ci
TESTING=true npm run build --if-present

- name: Save context
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: buildjet/cache/save@e376f15c6ec6dc595375c78633174c7e5f92dc0e # v3
with:
key: cypress-context-${{ github.run_id }}
path: ./
Expand All @@ -58,14 +72,15 @@ jobs:
strategy:
fail-fast: false
matrix:
# run multiple copies of the current job in parallel
containers: [1, 2, 3, 4, 5, 6, 7, 8]
# Run multiple copies of the current job in parallel
# Please increase the number or runners as your tests suite grows
containers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

name: runner ${{ matrix.containers }}

steps:
- name: Restore context
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: buildjet/cache/restore@e376f15c6ec6dc595375c78633174c7e5f92dc0e # v3
with:
fail-on-cache-miss: true
key: cypress-context-${{ github.run_id }}
Expand All @@ -74,20 +89,19 @@ jobs:
- name: Set up node ${{ needs.init.outputs.nodeVersion }}
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
cache: 'npm'
node-version: ${{ needs.init.outputs.nodeVersion }}

- name: Set up npm ${{ needs.init.outputs.npmVersion }}
run: npm i -g npm@"${{ needs.init.outputs.npmVersion }}"

- name: Pull server image
run: docker pull ghcr.io/nextcloud/continuous-integration-shallow-server

- name: Run E2E cypress tests
- name: Run ${{ matrix.containers == 'component' && 'component' || 'E2E' }} cypress tests
uses: cypress-io/github-action@db1693016f23ccf9043f4b2428f9b04e5d502a73 # v5.8.1
with:
record: true
parallel: true
# cypress run type
component: ${{ matrix.containers == 'component' }}
group: Run ${{ matrix.containers == 'component' && 'component' || 'E2E' }}
# cypress env
ci-build-id: ${{ github.sha }}-${{ github.run_number }}
tag: ${{ github.event_name }}
Expand All @@ -105,9 +119,20 @@ jobs:
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
if: always()
with:
name: snapshots
name: snapshots_${{ matrix.containers }}
path: cypress/snapshots

- name: Extract NC logs
if: failure() && matrix.containers != 'component'
run: docker logs nextcloud-cypress-tests-${{ env.APP_NAME }} > nextcloud.log

- name: Upload NC logs
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
if: failure() && matrix.containers != 'component'
with:
name: nc_logs_${{ matrix.containers }}
path: nextcloud.log

summary:
runs-on: ubuntu-latest
needs: [init, cypress]
Expand Down
9 changes: 5 additions & 4 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export default defineConfig({
// Needed to trigger `after:run` events with cypress open
experimentalInteractiveRunEvents: true,

// faster video processing
videoCompression: false,
// Faster processing, video is broken on GH actions anyway
video: false,

// Visual regression testing
env: {
Expand All @@ -39,7 +39,6 @@ export default defineConfig({
async setupNodeEvents(on, config) {
// Fix browserslist extend https://github.com/cypress-io/cypress/issues/2983#issuecomment-570616682
on('file:preprocessor', browserify())
// on('file:preprocessor', webpackPreprocessor({ webpackOptions }))
getCompareSnapshotsPlugin(on, config)

// Disable spell checking to prevent rendering differences
Expand All @@ -62,7 +61,9 @@ export default defineConfig({

// Remove container after run
on('after:run', () => {
stopNextcloud()
if (!process.env.CI) {
stopNextcloud()
}
})

// Before the browser launches
Expand Down
92 changes: 58 additions & 34 deletions cypress/dockerNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,44 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* eslint-disable no-console */
import type { Stream } from 'stream'
import type { Container } from 'dockerode'

import Docker from 'dockerode'
import waitOn from 'wait-on'
import path from 'path'
import waitOn from 'wait-on'

export const docker = new Docker()
const CONTAINER_NAME = 'nextcloud-cypress-tests'
import pkg from '../package.json'

const pkg = require('../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: string = 'master'): Promise<any> {
export const startNextcloud = async function(branch = 'master'): Promise<string> {
try {
// Pulling images
console.log('Pulling images...')
await docker.pull(SERVER_IMAGE)
console.log('Pulling images... ⏳')
await new Promise((resolve, reject) => docker.pull(SERVER_IMAGE, (_err, stream: Stream) => {
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... 🔍')
Expand All @@ -51,22 +67,22 @@ export const startNextcloud = async function (branch: string = 'master'): Promis
const oldContainer = docker.getContainer(CONTAINER_NAME)
const oldContainerData = await oldContainer.inspect()
if (oldContainerData.State.Running) {
console.log(`├─ Existing running container found`)
console.log('├─ Existing running container found')
if (localImage[0].Id !== oldContainerData.Image) {
console.log(`└─ But running container is outdated, replacing...`)
console.log('└─ But running container is outdated, replacing...')
} else {
// Get container's IP
console.log(`├─ Reusing that container`)
let ip = await getContainerIP(oldContainer)
console.log('├─ Reusing that container')
const ip = await getContainerIP(oldContainer)
return ip
}
} else {
console.log(`└─ None found!`)
console.log('└─ None found!')
}
// Forcing any remnants to be removed just in case
await oldContainer.remove({ force: true })
} catch (error) {
console.log(`└─ None found!`)
console.log('└─ None found!')
}

// Starting container
Expand All @@ -76,20 +92,22 @@ export const startNextcloud = async function (branch: string = 'master'): Promis
const container = await docker.createContainer({
Image: SERVER_IMAGE,
name: CONTAINER_NAME,
Env: [`BRANCH=${branch}`],
HostConfig: {
Binds: [`${APP_PATH}:/var/www/html/apps/${APP_NAME}`],
},
Env: [
`BRANCH=${branch}`,
],
})
await container.start()

// Get container's IP
let ip = await getContainerIP(container)
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('└─ Unable to start the container 🛑')
console.log(err)
stopNextcloud()
throw new Error('Unable to start the container')
Expand All @@ -99,7 +117,7 @@ export const startNextcloud = async function (branch: string = 'master'): Promis
/**
* Configure Nextcloud
*/
export const configureNextcloud = async function () {
export const configureNextcloud = async function() {
console.log('\nConfiguring nextcloud...')
const container = docker.getContainer(CONTAINER_NAME)
await runExec(container, ['php', 'occ', '--version'], true)
Expand All @@ -119,9 +137,9 @@ export const configureNextcloud = async function () {
}

/**
* Force stop the testing container
* Force stop the testing nextcloud container
*/
export const stopNextcloud = async function () {
export const stopNextcloud = async function() {
try {
const container = docker.getContainer(CONTAINER_NAME)
console.log('Stopping Nextcloud container...')
Expand All @@ -133,17 +151,19 @@ export const stopNextcloud = async function () {
}

/**
* Get the testing container's IP
* Get the testing container's IP address
*
* @param container the container to get the ip from
*/
export const getContainerIP = async function (
container = docker.getContainer(CONTAINER_NAME)
export const getContainerIP = async function(
container: Container = docker.getContainer(CONTAINER_NAME),
): Promise<string> {
let ip = ''
let tries = 0
while (ip === '' && tries < 10) {
tries++

await container.inspect(function (err, data) {
await container.inspect(function(_err, data) {
ip = data?.NetworkSettings?.IPAddress || ''
})

Expand All @@ -157,21 +177,25 @@ export const getContainerIP = async function (
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) {
/**
* 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 (
const runExec = async function(
container: Docker.Container,
command: string[],
verbose: boolean = false
verbose = false,
) {
const exec = await container.exec({
Cmd: command,
Expand All @@ -180,8 +204,8 @@ const runExec = async function (
User: 'www-data',
})

return new Promise((resolve, reject) => {
exec.start({}, (err, stream) => {
return new Promise((resolve) => {
exec.start({}, (_err, stream) => {
if (stream) {
stream.setEncoding('utf-8')
stream.on('data', str => {
Expand All @@ -195,6 +219,6 @@ const runExec = async function (
})
}

const sleep = function (milliseconds: number) {
const sleep = function(milliseconds: number) {
return new Promise((resolve) => setTimeout(resolve, milliseconds))
}