Merge pull request #52703 from nextcloud/feat/setup-checks

chore(cypress): add setup tests
pull/52693/head
John Molakvoæ 5 months ago committed by GitHub
commit ff1dfc6bd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 48
      .github/workflows/cypress.yml
  2. 6
      core/src/components/setup/RecommendedApps.vue
  3. 24
      cypress.config.ts
  4. 25
      cypress/dockerNode.ts
  5. 114
      cypress/e2e/core/setup.ts
  6. 4
      dist/core-recommendedapps.js
  7. 2
      dist/core-recommendedapps.js.map

@ -94,11 +94,56 @@ 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: ["component", '0', '1', '2', '3', '4', '5', '6', '7']
containers: ['component', 'setup', '0', '1', '2', '3', '4', '5', '6', '7']
# Hack as strategy.job-total includes the component and GitHub does not allow math expressions
# Always align this number with the total of e2e runners (max. index + 1)
total-containers: [8]
services:
mysql:
# Only start mysql if we are running the setup tests
image: ${{matrix.containers == 'setup' && 'ghcr.io/nextcloud/continuous-integration-mysql-8.4:latest' || ''}}
ports:
- '3306/tcp'
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_USER: oc_autotest
MYSQL_PASSWORD: nextcloud
MYSQL_DATABASE: oc_autotest
options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10
mariadb:
# Only start mariadb if we are running the setup tests
image: ${{matrix.containers == 'setup' && 'mariadb:11.4' || ''}}
ports:
- '3306/tcp'
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_USER: oc_autotest
MYSQL_PASSWORD: nextcloud
MYSQL_DATABASE: oc_autotest
options: --health-cmd="mariadb-admin ping" --health-interval 5s --health-timeout 2s --health-retries 5
postgres:
# Only start postgres if we are running the setup tests
image: ${{matrix.containers == 'setup' && 'ghcr.io/nextcloud/continuous-integration-postgres-17:latest' || ''}}
ports:
- '5432/tcp'
env:
POSTGRES_USER: root
POSTGRES_PASSWORD: rootpassword
POSTGRES_DB: nextcloud
options: --mount type=tmpfs,destination=/var/lib/postgresql/data --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
oracle:
# Only start oracle if we are running the setup tests
image: ${{matrix.containers == 'setup' && 'ghcr.io/gvenzl/oracle-free:23' || ''}}
ports:
- '1521'
env:
ORACLE_PASSWORD: oracle
options: --health-cmd healthcheck.sh --health-interval 20s --health-timeout 10s --health-retries 10
name: runner ${{ matrix.containers }}
steps:
@ -141,6 +186,7 @@ jobs:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
SPLIT: ${{ matrix.total-containers }}
SPLIT_INDEX: ${{ matrix.containers == 'component' && 0 || matrix.containers }}
SETUP_TESTING: ${{ matrix.containers == 'setup' && 'true' || '' }}
- name: Upload snapshots and videos
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2

@ -4,7 +4,7 @@
-->
<template>
<div class="guest-box">
<div class="guest-box" data-cy-setup-recommended-apps>
<h2>{{ t('core', 'Recommended apps') }}</h2>
<p v-if="loadingApps" class="loading text-center">
{{ t('core', 'Loading apps …') }}
@ -40,13 +40,15 @@
<NcButton v-if="showInstallButton && !installingApps"
type="tertiary"
role="link"
:href="defaultPageUrl">
:href="defaultPageUrl"
data-cy-setup-recommended-apps-skip>
{{ t('core', 'Skip') }}
</NcButton>
<NcButton v-if="showInstallButton"
type="primary"
:disabled="installingApps || !isAnyAppSelected"
data-cy-setup-recommended-apps-install>
@click.stop.prevent="installApps">
{{ installingApps ? t('core', 'Installing apps …') : t('core', 'Install recommended apps') }}
</NcButton>

@ -3,6 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Configuration } from 'webpack'
import { defineConfig } from 'cypress'
import { join } from 'path'
import { removeDirectory } from 'cypress-delete-downloads-folder'
import cypressSplit from 'cypress-split'
import webpackPreprocessor from '@cypress/webpack-preprocessor'
import {
applyChangesToNextcloud,
configureNextcloud,
@ -10,11 +17,6 @@ import {
stopNextcloud,
waitOnNextcloud,
} from './cypress/dockerNode'
import { defineConfig } from 'cypress'
import cypressSplit from 'cypress-split'
import { removeDirectory } from 'cypress-delete-downloads-folder'
import webpackPreprocessor from '@cypress/webpack-preprocessor'
import webpackConfig from './webpack.config.js'
export default defineConfig({
@ -62,8 +64,6 @@ export default defineConfig({
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
async setupNodeEvents(on, config) {
cypressSplit(on, config)
on('file:preprocessor', webpackPreprocessor({ webpackOptions: webpackConfig as Configuration }))
on('task', { removeDirectory })
@ -106,6 +106,16 @@ export default defineConfig({
}
})
// Check if we are running the setup checks
if (process.env.SETUP_TESTING === 'true') {
console.log('Adding setup tests to specPattern 🧮')
config.specPattern = [join(__dirname, 'cypress/e2e/core/setup.ts')]
console.log('└─ Done')
} else {
// If we are not running the setup tests, we need to remove the setup tests from the specPattern
cypressSplit(on, config)
}
// Before the browser launches
// starting Nextcloud testing container
const ip = await startNextcloud(process.env.BRANCH)

@ -94,6 +94,9 @@ export const startNextcloud = async function(branch: string = getCurrentGitBranc
HostPort: '8083',
}],
},
// If running the setup tests, let's bind to host
// to communicate with the github actions DB services
NetworkMode: process.env.SETUP_TESTING === 'true' ? await getGithubNetwork() : undefined,
},
Env: [
`BRANCH=${branch}`,
@ -106,9 +109,6 @@ export const startNextcloud = async function(branch: string = getCurrentGitBranc
await runExec(container, ['chown', '-R', 'www-data:www-data', '/var/www/html/data'], false, 'root')
await runExec(container, ['chmod', '0770', '/var/www/html/data'], false, 'root')
// Init Nextcloud
// await runExec(container, ['initnc.sh'], true, 'root')
// Get container's IP
const ip = await getContainerIP(container)
@ -252,6 +252,7 @@ export const getContainerIP = async function(
if (err) {
throw err
}
if (data?.HostConfig.PortBindings?.['80/tcp']?.[0]?.HostPort) {
ip = `localhost:${data.HostConfig.PortBindings['80/tcp'][0].HostPort}`
} else {
@ -335,3 +336,21 @@ const sleep = function(milliseconds: number) {
const getCurrentGitBranch = function() {
return execSync('git rev-parse --abbrev-ref HEAD').toString().trim() || 'master'
}
/**
* Get the network name of the github actions network
* This is used to connect to the database services
* started by github actions
*/
const getGithubNetwork = async function(): Promise<string|undefined> {
console.log('├─ Looking for github actions network... 🔍')
const networks = await docker.listNetworks()
const network = networks.find((network) => network.Name.startsWith('github_network'))
if (network) {
console.log('│ └─ Found github actions network: ' + network.Name)
return network.Name
}
console.log('│ └─ No github actions network found')
return undefined
}

@ -0,0 +1,114 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
/**
* DO NOT RENAME THIS FILE to .cy.ts
* This is not following the pattern of the other files in this folder
* because it is manually added to the tests by the cypress config.
*/
describe('Can install Nextcloud', { testIsolation: true, retries: 0 }, () => {
beforeEach(() => {
// Move the config file and data folder
cy.runCommand('rm /var/www/html/config/config.php', { failOnNonZeroExit: false })
cy.runCommand('rm /var/www/html/data/owncloud.db', { failOnNonZeroExit: false })
})
it('Sqlite', () => {
cy.visit('/')
cy.get('[data-cy-setup-form]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminlogin"]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminpass"]').should('be.visible')
cy.get('[data-cy-setup-form-field="directory"]').should('have.value', '/var/www/html/data')
// Select the SQLite database
cy.get('[data-cy-setup-form-field="dbtype-sqlite"] input').check({ force: true })
sharedSetup()
})
it('MySQL', () => {
cy.visit('/')
cy.get('[data-cy-setup-form]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminlogin"]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminpass"]').should('be.visible')
cy.get('[data-cy-setup-form-field="directory"]').should('have.value', '/var/www/html/data')
// Select the SQLite database
cy.get('[data-cy-setup-form-field="dbtype-mysql"] input').check({ force: true })
// Fill in the DB form
cy.get('[data-cy-setup-form-field="dbuser"]').type('{selectAll}oc_autotest')
cy.get('[data-cy-setup-form-field="dbpass"]').type('{selectAll}nextcloud')
cy.get('[data-cy-setup-form-field="dbname"]').type('{selectAll}oc_autotest')
cy.get('[data-cy-setup-form-field="dbhost"]').type('{selectAll}mysql:3306')
sharedSetup()
})
it('MariaDB', () => {
cy.visit('/')
cy.get('[data-cy-setup-form]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminlogin"]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminpass"]').should('be.visible')
cy.get('[data-cy-setup-form-field="directory"]').should('have.value', '/var/www/html/data')
// Select the SQLite database
cy.get('[data-cy-setup-form-field="dbtype-mysql"] input').check({ force: true })
// Fill in the DB form
cy.get('[data-cy-setup-form-field="dbuser"]').type('{selectAll}oc_autotest')
cy.get('[data-cy-setup-form-field="dbpass"]').type('{selectAll}nextcloud')
cy.get('[data-cy-setup-form-field="dbname"]').type('{selectAll}oc_autotest')
cy.get('[data-cy-setup-form-field="dbhost"]').type('{selectAll}mariadb:3306')
sharedSetup()
})
it('PostgreSQL', () => {
cy.visit('/')
cy.get('[data-cy-setup-form]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminlogin"]').should('be.visible')
cy.get('[data-cy-setup-form-field="adminpass"]').should('be.visible')
cy.get('[data-cy-setup-form-field="directory"]').should('have.value', '/var/www/html/data')
// Select the SQLite database
cy.get('[data-cy-setup-form-field="dbtype-pgsql"] input').check({ force: true })
// Fill in the DB form
cy.get('[data-cy-setup-form-field="dbuser"]').type('{selectAll}root')
cy.get('[data-cy-setup-form-field="dbpass"]').type('{selectAll}rootpassword')
cy.get('[data-cy-setup-form-field="dbname"]').type('{selectAll}nextcloud')
cy.get('[data-cy-setup-form-field="dbhost"]').type('{selectAll}postgres:5432')
sharedSetup()
})
})
/**
* Shared admin setup function for the Nextcloud setup
*/
function sharedSetup() {
const randAdmin = 'admin-' + Math.random().toString(36).substring(2, 15)
// Fill in the form
cy.get('[data-cy-setup-form-field="adminlogin"]').type(randAdmin)
cy.get('[data-cy-setup-form-field="adminpass"]').type(randAdmin)
// Nothing more to do on sqlite, let's continue
cy.get('[data-cy-setup-form-submit]').click()
// Wait for the setup to finish
cy.location('pathname', { timeout: 10000 })
.should('include', '/core/apps/recommended')
// Skip the setup apps
cy.get('[data-cy-setup-recommended-apps]').should('be.visible')
cy.get('[data-cy-setup-recommended-apps-skip]').click()
// Go to files
cy.visit('/apps/files/')
cy.get('[data-cy-files-content]').should('be.visible')
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save