Skip to content

Commit 959daed

Browse files
Merge pull request #7379 from nextcloud/backport/7283/stable32
[stable32] ci(cypress): Use cypress-split for running tests in parallel
2 parents d3c07c7 + bf86f54 commit 959daed

File tree

6 files changed

+3510
-1497
lines changed

6 files changed

+3510
-1497
lines changed

.github/workflows/cypress-e2e.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ jobs:
2020
fail-fast: false
2121
matrix:
2222
node-version: [20.x]
23-
# containers: [1, 2, 3]
23+
containers: [1, 2, 3]
2424
php-versions: [ '8.2' ]
2525
server-versions: [ 'stable32' ]
2626

27+
name: runner ${{ matrix.containers }}
28+
2729
env:
2830
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, apcu
2931
key: cache-v1
@@ -135,6 +137,8 @@ jobs:
135137
env:
136138
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
137139
npm_package_name: ${{ env.APP_NAME }}
140+
SPLIT: ${{ strategy.job-total }}
141+
SPLIT_INDEX: ${{ strategy.job-index }}
138142

139143
- name: Upload test failure screenshots
140144
uses: actions/upload-artifact@v4
@@ -151,3 +155,15 @@ jobs:
151155
name: Upload nextcloud log
152156
path: data/nextcloud.log
153157
retention-days: 5
158+
159+
summary:
160+
runs-on: ubuntu-latest-low
161+
needs: [ cypress ]
162+
163+
if: always()
164+
165+
name: cypress-summary
166+
167+
steps:
168+
- name: Summary status
169+
run: if ${{ needs.cypress.result != 'success' && needs.cypress.result != 'skipped' }}; then exit 1; fi

cypress.config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { defineConfig } = require('cypress')
2+
const cypressSplit = require('cypress-split')
23

34
module.exports = defineConfig({
45
projectId: '1s7wkc',
@@ -8,9 +9,12 @@ module.exports = defineConfig({
89
// We've imported your old cypress plugins here.
910
// You may want to clean this up later by importing these.
1011
setupNodeEvents(on, config) {
11-
return require('./cypress/plugins/index.js')(on, config)
12+
cypressSplit(on, config)
13+
require('./cypress/plugins/index.js')(on, config)
14+
return config
1215
},
1316
baseUrl: 'http://nextcloud.local/index.php',
1417
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
18+
experimentalMemoryManagement: true,
1519
},
1620
})

cypress/e2e/cardActions.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
import { randUser } from '../utils/index.js'
6+
import { sampleBoard } from '../utils/sampleBoard'
7+
8+
const user = randUser()
9+
const boardData = sampleBoard()
10+
11+
const auth = {
12+
user: user.userId,
13+
password: user.password,
14+
}
15+
16+
const useModal = (useModal) => {
17+
return cy.request({
18+
method: 'POST',
19+
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
20+
auth,
21+
body: { value: useModal },
22+
}).then((response) => {
23+
expect(response.status).to.eq(200)
24+
})
25+
}
26+
27+
describe('Card actions', function () {
28+
let boardId
29+
before(function () {
30+
cy.createUser(user)
31+
cy.login(user)
32+
cy.createExampleBoard({
33+
user,
34+
board: boardData,
35+
}).then((board) => {
36+
boardId = board.id
37+
})
38+
})
39+
40+
beforeEach(function () {
41+
cy.login(user)
42+
useModal(false).then(() => {
43+
cy.visit(`/apps/deck/#/board/${boardId}`)
44+
})
45+
})
46+
47+
it('Custom card actions', () => {
48+
const myAction = {
49+
label: 'Test action',
50+
icon: 'icon-user',
51+
callback(card) {
52+
console.log('Called callback', card)
53+
},
54+
}
55+
cy.spy(myAction, 'callback').as('myAction.callback')
56+
57+
cy.window().then(win => {
58+
win.OCA.Deck.registerCardAction(myAction)
59+
})
60+
61+
cy.get('.card:contains("Hello world")').should('be.visible').click()
62+
cy.get('#app-sidebar-vue')
63+
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
64+
65+
cy.get('.app-sidebar-header .action-item__menutoggle').click()
66+
cy.get('.v-popper__popper button:contains("Test action")').click()
67+
68+
cy.get('@myAction.callback')
69+
.should('be.called')
70+
.its('firstCall.args.0')
71+
.as('args')
72+
73+
cy.url().then(url => {
74+
const cardId = url.split('/').pop()
75+
cy.get('@args').should('have.property', 'name', 'Hello world')
76+
cy.get('@args').should('have.property', 'stackname', 'TestList')
77+
cy.get('@args').should('have.property', 'boardname', 'MyTestBoard')
78+
cy.get('@args').its('link').then((url) => {
79+
expect(url.split('/').pop() === cardId).to.be.true
80+
cy.visit(url)
81+
cy.get('#app-sidebar-vue')
82+
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
83+
})
84+
})
85+
})
86+
87+
it('clone card', () => {
88+
cy.intercept({ method: 'POST', url: '**/apps/deck/**/cards/*/clone' }).as('clone')
89+
cy.get('.card:contains("Hello world")').should('be.visible').click()
90+
cy.get('#app-sidebar-vue')
91+
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
92+
93+
cy.get('.app-sidebar-header .action-item__menutoggle').click()
94+
cy.get('.v-popper__popper button:contains("Move/copy card")').click()
95+
cy.get('.vs__dropdown-menu span[title="MyTestBoard"]').should('be.visible').click()
96+
cy.wait(3000) // wait for select component to load stacks
97+
cy.get('[data-cy="select-stack"] .vs__dropdown-toggle').should('be.visible').click()
98+
cy.get('.vs__dropdown-menu span[title="TestList"]').should('be.visible').click()
99+
cy.get('.modal-container button:contains("Copy card")').click()
100+
cy.wait('@clone', { timeout: 7000 })
101+
cy.get('.card:contains("Hello world")').should('have.length', 2)
102+
})
103+
})

cypress/e2e/cardFeatures.js

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -321,69 +321,4 @@ describe('Card', function () {
321321
})
322322

323323
})
324-
325-
describe('Card actions', () => {
326-
beforeEach(function () {
327-
cy.login(user)
328-
useModal(false).then(() => {
329-
cy.visit(`/apps/deck/#/board/${boardId}`)
330-
})
331-
})
332-
333-
it('Custom card actions', () => {
334-
const myAction = {
335-
label: 'Test action',
336-
icon: 'icon-user',
337-
callback(card) {
338-
console.log('Called callback', card)
339-
},
340-
}
341-
cy.spy(myAction, 'callback').as('myAction.callback')
342-
343-
cy.window().then(win => {
344-
win.OCA.Deck.registerCardAction(myAction)
345-
})
346-
347-
cy.get('.card:contains("Hello world")').should('be.visible').click()
348-
cy.get('#app-sidebar-vue')
349-
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
350-
351-
cy.get('.app-sidebar-header .action-item__menutoggle').click()
352-
cy.get('.v-popper__popper button:contains("Test action")').click()
353-
354-
cy.get('@myAction.callback')
355-
.should('be.called')
356-
.its('firstCall.args.0')
357-
.as('args')
358-
359-
cy.url().then(url => {
360-
const cardId = url.split('/').pop()
361-
cy.get('@args').should('have.property', 'name', 'Hello world')
362-
cy.get('@args').should('have.property', 'stackname', 'TestList')
363-
cy.get('@args').should('have.property', 'boardname', 'MyTestBoard')
364-
cy.get('@args').its('link').then((url) => {
365-
expect(url.split('/').pop() === cardId).to.be.true
366-
cy.visit(url)
367-
cy.get('#app-sidebar-vue')
368-
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
369-
})
370-
})
371-
})
372-
373-
it('clone card', () => {
374-
cy.intercept({ method: 'POST', url: '**/apps/deck/**/cards/*/clone' }).as('clone')
375-
cy.get('.card:contains("Hello world")').should('be.visible').click()
376-
cy.get('#app-sidebar-vue')
377-
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
378-
379-
cy.get('.app-sidebar-header .action-item__menutoggle').click()
380-
cy.get('.v-popper__popper button:contains("Move/copy card")').click()
381-
cy.get('.vs__dropdown-menu span[title="MyTestBoard"]').should('be.visible').click()
382-
cy.get('[data-cy="select-stack"] .vs__dropdown-toggle').should('be.visible').click()
383-
cy.get('.vs__dropdown-menu span[title="TestList"]').should('be.visible').click()
384-
cy.get('.modal-container button:contains("Copy card")').click()
385-
cy.wait('@clone', { timeout: 7000 })
386-
cy.get('.card:contains("Hello world")').should('have.length', 2)
387-
})
388-
})
389324
})

0 commit comments

Comments
 (0)