Compare commits

..

2 Commits

Author SHA1 Message Date
Luka Trovic
3abdb438c0 ci(cypress): add summary job
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2025-10-02 10:28:19 +02:00
Luka Trovic
1b9b80a9b2 chore: enable experimentalMemoryManagement for cypress test
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2025-10-02 10:28:19 +02:00
16 changed files with 2299 additions and 405 deletions

View File

@@ -20,10 +20,12 @@ jobs:
fail-fast: false
matrix:
node-version: [20.x]
# containers: [1, 2, 3]
containers: [1, 2, 3]
php-versions: [ '8.2' ]
server-versions: [ 'master' ]
name: runner ${{ matrix.containers }}
env:
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, apcu
key: cache-v1
@@ -135,6 +137,8 @@ jobs:
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
npm_package_name: ${{ env.APP_NAME }}
SPLIT: ${{ strategy.job-total }}
SPLIT_INDEX: ${{ strategy.job-index }}
- name: Upload test failure screenshots
uses: actions/upload-artifact@v4
@@ -151,3 +155,15 @@ jobs:
name: Upload nextcloud log
path: data/nextcloud.log
retention-days: 5
summary:
runs-on: ubuntu-latest-low
needs: [ cypress ]
if: always()
name: cypress-summary
steps:
- name: Summary status
run: if ${{ needs.cypress.result != 'success' && needs.cypress.result != 'skipped' }}; then exit 1; fi

View File

@@ -24,7 +24,7 @@ Deck is a kanban style organization tool aimed at personal planning and project
### Mobile apps
- [Nextcloud Deck app for Android](https://github.com/stefan-niedermann/nextcloud-deck) - It is available in [F-Droid](https://f-droid.org/de/packages/it.niedermann.nextcloud.deck/) and the [Google Play Store](https://play.google.com/store/apps/details?id=it.niedermann.nextcloud.deck.play)
- Nextcloud Deck app for iOS - It is available in [Apple App store](https://apps.apple.com/de/app/next-deck/id6752478755)
- [Nextcloud Deck app for iOS](https://github.com/StCyr/deck-react-native) - It is available in [Apple App store](https://apps.apple.com/ml/app/nextcloud-deck/id1570892788)
### 3rd-Party Integrations

View File

@@ -1,4 +1,5 @@
const { defineConfig } = require('cypress')
const cypressSplit = require('cypress-split')
module.exports = defineConfig({
projectId: '1s7wkc',
@@ -8,9 +9,12 @@ module.exports = defineConfig({
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
cypressSplit(on, config)
require('./cypress/plugins/index.js')(on, config)
return config
},
baseUrl: 'http://nextcloud.local/index.php',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
experimentalMemoryManagement: true,
},
})

103
cypress/e2e/cardActions.js Normal file
View File

@@ -0,0 +1,103 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { randUser } from '../utils/index.js'
import { sampleBoard } from '../utils/sampleBoard'
const user = randUser()
const boardData = sampleBoard()
const auth = {
user: user.userId,
password: user.password,
}
const useModal = (useModal) => {
return cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
auth,
body: { value: useModal },
}).then((response) => {
expect(response.status).to.eq(200)
})
}
describe('Card actions', function () {
let boardId
before(function () {
cy.createUser(user)
cy.login(user)
cy.createExampleBoard({
user,
board: boardData,
}).then((board) => {
boardId = board.id
})
})
beforeEach(function () {
cy.login(user)
useModal(false).then(() => {
cy.visit(`/apps/deck/#/board/${boardId}`)
})
})
it('Custom card actions', () => {
const myAction = {
label: 'Test action',
icon: 'icon-user',
callback(card) {
console.log('Called callback', card)
},
}
cy.spy(myAction, 'callback').as('myAction.callback')
cy.window().then(win => {
win.OCA.Deck.registerCardAction(myAction)
})
cy.get('.card:contains("Hello world")').should('be.visible').click()
cy.get('#app-sidebar-vue')
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
cy.get('.app-sidebar-header .action-item__menutoggle').click()
cy.get('.v-popper__popper button:contains("Test action")').click()
cy.get('@myAction.callback')
.should('be.called')
.its('firstCall.args.0')
.as('args')
cy.url().then(url => {
const cardId = url.split('/').pop()
cy.get('@args').should('have.property', 'name', 'Hello world')
cy.get('@args').should('have.property', 'stackname', 'TestList')
cy.get('@args').should('have.property', 'boardname', 'MyTestBoard')
cy.get('@args').its('link').then((url) => {
expect(url.split('/').pop() === cardId).to.be.true
cy.visit(url)
cy.get('#app-sidebar-vue')
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
})
})
})
it('clone card', () => {
cy.intercept({ method: 'POST', url: '**/apps/deck/**/cards/*/clone' }).as('clone')
cy.get('.card:contains("Hello world")').should('be.visible').click()
cy.get('#app-sidebar-vue')
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
cy.get('.app-sidebar-header .action-item__menutoggle').click()
cy.get('.v-popper__popper button:contains("Move/copy card")').click()
cy.get('.vs__dropdown-menu span[title="MyTestBoard"]').should('be.visible').click()
cy.wait(3000) // wait for select component to load stacks
cy.get('[data-cy="select-stack"] .vs__dropdown-toggle').should('be.visible').click()
cy.get('.vs__dropdown-menu span[title="TestList"]').should('be.visible').click()
cy.get('.modal-container button:contains("Copy card")').click()
cy.wait('@clone', { timeout: 7000 })
cy.get('.card:contains("Hello world")').should('have.length', 2)
})
})

View File

@@ -321,69 +321,4 @@ describe('Card', function () {
})
})
describe('Card actions', () => {
beforeEach(function () {
cy.login(user)
useModal(false).then(() => {
cy.visit(`/apps/deck/#/board/${boardId}`)
})
})
it('Custom card actions', () => {
const myAction = {
label: 'Test action',
icon: 'icon-user',
callback(card) {
console.log('Called callback', card)
},
}
cy.spy(myAction, 'callback').as('myAction.callback')
cy.window().then(win => {
win.OCA.Deck.registerCardAction(myAction)
})
cy.get('.card:contains("Hello world")').should('be.visible').click()
cy.get('#app-sidebar-vue')
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
cy.get('.app-sidebar-header .action-item__menutoggle').click()
cy.get('.v-popper__popper button:contains("Test action")').click()
cy.get('@myAction.callback')
.should('be.called')
.its('firstCall.args.0')
.as('args')
cy.url().then(url => {
const cardId = url.split('/').pop()
cy.get('@args').should('have.property', 'name', 'Hello world')
cy.get('@args').should('have.property', 'stackname', 'TestList')
cy.get('@args').should('have.property', 'boardname', 'MyTestBoard')
cy.get('@args').its('link').then((url) => {
expect(url.split('/').pop() === cardId).to.be.true
cy.visit(url)
cy.get('#app-sidebar-vue')
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
})
})
})
it('clone card', () => {
cy.intercept({ method: 'POST', url: '**/apps/deck/**/cards/*/clone' }).as('clone')
cy.get('.card:contains("Hello world")').should('be.visible').click()
cy.get('#app-sidebar-vue')
.find('.ProseMirror h1').contains('Hello world').should('be.visible')
cy.get('.app-sidebar-header .action-item__menutoggle').click()
cy.get('.v-popper__popper button:contains("Move/copy card")').click()
cy.get('.vs__dropdown-menu span[title="MyTestBoard"]').should('be.visible').click()
cy.get('[data-cy="select-stack"] .vs__dropdown-toggle').should('be.visible').click()
cy.get('.vs__dropdown-menu span[title="TestList"]').should('be.visible').click()
cy.get('.modal-container button:contains("Copy card")').click()
cy.wait('@clone', { timeout: 7000 })
cy.get('.card:contains("Hello world")').should('have.length', 2)
})
})
})

View File

@@ -84,9 +84,6 @@ OC.L10N.register(
"Failed to upload {name}" : "Не ўдалося запампаваць {name}",
"Share" : "Абагуліць",
"Personal" : "Асабістыя",
"Example Task 3" : "Прыклад задання 3",
"Example Task 2" : "Прыклад задання 2",
"Example Task 1" : "Прыклад задання 1",
"Today" : "Сёння",
"Tomorrow" : "Заўтра"
},

View File

@@ -82,9 +82,6 @@
"Failed to upload {name}" : "Не ўдалося запампаваць {name}",
"Share" : "Абагуліць",
"Personal" : "Асабістыя",
"Example Task 3" : "Прыклад задання 3",
"Example Task 2" : "Прыклад задання 2",
"Example Task 1" : "Прыклад задання 1",
"Today" : "Сёння",
"Tomorrow" : "Заўтра"
},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"

View File

@@ -373,7 +373,6 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Megjegyzés: Csak a JSON formátum támogatott a Kártyák alkalmazásba való importáláskor.",
"Export" : "Exportálás",
"Loading filtered view" : "Szűrt nézet betöltése",
"Search for {searchQuery} in other boards" : "Keresés a(z) {searchQuery} kifejezésre a többi táblában",
"Search for {searchQuery} in all boards" : "Keresés a(z) {searchQuery} kifejezésre az összes táblában",
"No results found" : "Nincs találat",
"Deck board {name}\n* Last modified on {lastMod}" : "{name} kártyatábla\n* Legutóbb módosítva: {lastMod}",

View File

@@ -371,7 +371,6 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Megjegyzés: Csak a JSON formátum támogatott a Kártyák alkalmazásba való importáláskor.",
"Export" : "Exportálás",
"Loading filtered view" : "Szűrt nézet betöltése",
"Search for {searchQuery} in other boards" : "Keresés a(z) {searchQuery} kifejezésre a többi táblában",
"Search for {searchQuery} in all boards" : "Keresés a(z) {searchQuery} kifejezésre az összes táblában",
"No results found" : "Nincs találat",
"Deck board {name}\n* Last modified on {lastMod}" : "{name} kártyatábla\n* Legutóbb módosítva: {lastMod}",

View File

@@ -81,14 +81,10 @@ OC.L10N.register(
"Could not write file to disk" : "Неможе да се запишува на дискот",
"A PHP extension stopped the file upload" : "PHP додаток го стопираше прикачувањето на датотеката",
"No file uploaded or file size exceeds maximum of %s" : "Нема прикачена дадотека или големината го надмминува максимумот од %s",
"Invalid file type. Only JSON files are allowed." : "Невалиден тип на датотека. Дозволени се само JSON датотеки.",
"Invalid JSON data" : "Невалидни JSON податоци",
"Failed to import board" : "Неуспешен увоз на табла",
"Cards due today" : "Картици со рок до денес",
"Cards due tomorrow" : "Картици со рок до утре",
"Upcoming cards" : "Престојни картици",
"Load more" : "Вчитај повеќе",
"Welcome to Nextcloud Deck!" : "Добредојдовте во Nextcloud Deck!",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Картицата \"%s\" на \"%s\" ти е доделена од %s.",
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} ти ја додели картицата {deck-card} на {deck-board}.",
"The card \"%s\" on \"%s\" has reached its due date." : "Картицата \"%s\" на \"%s\" го достигна датумот на истекување.",
@@ -112,15 +108,9 @@ OC.L10N.register(
"Later" : "Покасно",
"copy" : "копирај",
"Read more inside" : "Прочитај повеќе",
"Custom lists - click to rename!" : "Прилагодени листи кликнете за преименување!",
"To Do" : "За правење",
"In Progress" : "Во тек",
"Done" : "Готово",
"1. Open to learn more about boards and cards" : "1. Отворете за да дознаете повеќе за таблите и картичките",
"2. Drag cards left and right, up and down" : "2. Влечете ги картичките лево и десно, горе и долу",
"3. Apply rich formatting and link content" : "3. Применете богато форматирање и поврзете содржина",
"4. Share, comment and collaborate!" : "4. Споделувајте, коментирајте и соработувајте!",
"Create your first card!" : "Креирајте ја вашата прва картичка!",
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Коментарот има повеќе од %s карактери.\nДодаден е како пролог на картицата со име %s.\nДостапен е на линк: %s.",
"Attachments" : "Прилози",
"File" : "Датотека",
@@ -162,7 +152,6 @@ OC.L10N.register(
"Filter by assigned user" : "Филтрирај по назначени корисници",
"Unassigned" : "Неназначени",
"Filter by status" : "Филтрирај по статус",
"Open and completed" : "Отворени и завршени",
"Open" : "Отвори",
"Completed" : "Завршено",
"Filter by due date" : "Филтрирај по краен рок",
@@ -288,7 +277,7 @@ OC.L10N.register(
"Create a new tag:" : "Направи нова ознака:",
"(group)" : "(group)",
"{count} comments, {unread} unread" : "{count} коментари, {unread} непрочитани",
"Todo items" : "Задачи",
"Todo items" : "Работи за правење",
"Edit card title" : "Измени наслов на картица",
"Open link" : "Отвори линк",
"Card deleted" : "Картицата е избришана",
@@ -305,9 +294,7 @@ OC.L10N.register(
"Action" : "Акција",
"Shift" : "Shift",
"Scroll" : "Scroll",
"Scroll sideways" : "Лизгај странично",
"Navigate between cards" : "Навигација помеѓу картиците",
"Esc" : "Esc",
"Close card details" : "Затвори детали на картица",
"Ctrl" : "Ctrl",
"Search" : "Барај",
@@ -355,9 +342,6 @@ OC.L10N.register(
"Assigned cards" : "Доделени картици",
"No notifications" : "Нема известувања",
"Delete board" : "Избриши табла",
"Importing board..." : "Увезување табла...",
"Board imported successfully" : "Таблата е успешно увезена",
"Import board" : "Увези табла",
"Clone {boardTitle}" : "Клонирај {boardTitle}",
"Clone cards" : "Клинирај картици",
"Clone assignments" : "Клонирај задачи",
@@ -368,9 +352,6 @@ OC.L10N.register(
"Restore archived cards" : "Врати архивирани картици",
"Clone" : "Клонирај",
"Export {boardTitle}" : "Извези {boardTitle}",
"Export as JSON" : "Извези како JSON",
"Export as CSV" : "Извези како CSV",
"Note: Only the JSON format is supported for importing back into the Deck app." : "Забелешка: Поддржан е само JSON формат за увоз назад во апликацијата Deck.",
"Export" : "Извези",
"Loading filtered view" : "Вчитување на филтриран поглед",
"Search for {searchQuery} in other boards" : "Барај {searchQuery} во други табли",

View File

@@ -79,14 +79,10 @@
"Could not write file to disk" : "Неможе да се запишува на дискот",
"A PHP extension stopped the file upload" : "PHP додаток го стопираше прикачувањето на датотеката",
"No file uploaded or file size exceeds maximum of %s" : "Нема прикачена дадотека или големината го надмминува максимумот од %s",
"Invalid file type. Only JSON files are allowed." : "Невалиден тип на датотека. Дозволени се само JSON датотеки.",
"Invalid JSON data" : "Невалидни JSON податоци",
"Failed to import board" : "Неуспешен увоз на табла",
"Cards due today" : "Картици со рок до денес",
"Cards due tomorrow" : "Картици со рок до утре",
"Upcoming cards" : "Престојни картици",
"Load more" : "Вчитај повеќе",
"Welcome to Nextcloud Deck!" : "Добредојдовте во Nextcloud Deck!",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Картицата \"%s\" на \"%s\" ти е доделена од %s.",
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} ти ја додели картицата {deck-card} на {deck-board}.",
"The card \"%s\" on \"%s\" has reached its due date." : "Картицата \"%s\" на \"%s\" го достигна датумот на истекување.",
@@ -110,15 +106,9 @@
"Later" : "Покасно",
"copy" : "копирај",
"Read more inside" : "Прочитај повеќе",
"Custom lists - click to rename!" : "Прилагодени листи кликнете за преименување!",
"To Do" : "За правење",
"In Progress" : "Во тек",
"Done" : "Готово",
"1. Open to learn more about boards and cards" : "1. Отворете за да дознаете повеќе за таблите и картичките",
"2. Drag cards left and right, up and down" : "2. Влечете ги картичките лево и десно, горе и долу",
"3. Apply rich formatting and link content" : "3. Применете богато форматирање и поврзете содржина",
"4. Share, comment and collaborate!" : "4. Споделувајте, коментирајте и соработувајте!",
"Create your first card!" : "Креирајте ја вашата прва картичка!",
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Коментарот има повеќе од %s карактери.\nДодаден е како пролог на картицата со име %s.\nДостапен е на линк: %s.",
"Attachments" : "Прилози",
"File" : "Датотека",
@@ -160,7 +150,6 @@
"Filter by assigned user" : "Филтрирај по назначени корисници",
"Unassigned" : "Неназначени",
"Filter by status" : "Филтрирај по статус",
"Open and completed" : "Отворени и завршени",
"Open" : "Отвори",
"Completed" : "Завршено",
"Filter by due date" : "Филтрирај по краен рок",
@@ -286,7 +275,7 @@
"Create a new tag:" : "Направи нова ознака:",
"(group)" : "(group)",
"{count} comments, {unread} unread" : "{count} коментари, {unread} непрочитани",
"Todo items" : "Задачи",
"Todo items" : "Работи за правење",
"Edit card title" : "Измени наслов на картица",
"Open link" : "Отвори линк",
"Card deleted" : "Картицата е избришана",
@@ -303,9 +292,7 @@
"Action" : "Акција",
"Shift" : "Shift",
"Scroll" : "Scroll",
"Scroll sideways" : "Лизгај странично",
"Navigate between cards" : "Навигација помеѓу картиците",
"Esc" : "Esc",
"Close card details" : "Затвори детали на картица",
"Ctrl" : "Ctrl",
"Search" : "Барај",
@@ -353,9 +340,6 @@
"Assigned cards" : "Доделени картици",
"No notifications" : "Нема известувања",
"Delete board" : "Избриши табла",
"Importing board..." : "Увезување табла...",
"Board imported successfully" : "Таблата е успешно увезена",
"Import board" : "Увези табла",
"Clone {boardTitle}" : "Клонирај {boardTitle}",
"Clone cards" : "Клинирај картици",
"Clone assignments" : "Клонирај задачи",
@@ -366,9 +350,6 @@
"Restore archived cards" : "Врати архивирани картици",
"Clone" : "Клонирај",
"Export {boardTitle}" : "Извези {boardTitle}",
"Export as JSON" : "Извези како JSON",
"Export as CSV" : "Извези како CSV",
"Note: Only the JSON format is supported for importing back into the Deck app." : "Забелешка: Поддржан е само JSON формат за увоз назад во апликацијата Deck.",
"Export" : "Извези",
"Loading filtered view" : "Вчитување на филтриран поглед",
"Search for {searchQuery} in other boards" : "Барај {searchQuery} во други табли",

View File

@@ -266,7 +266,6 @@ OC.L10N.register(
"{count} comments, {unread} unread" : "{count} reacties, {unread} ongelezen",
"Todo items" : "Te doen onderwerpen",
"Edit card title" : "Wijzig de titel van de kaart",
"Open link" : "Open link",
"Card deleted" : "Kaart verwijderd",
"Edit title" : "Titel bewerken",
"Assign to me" : "Aan mij toewijzen",
@@ -280,7 +279,6 @@ OC.L10N.register(
"Shift" : "Shift",
"Ctrl" : "Ctrl",
"Search" : "Zoeken",
"Enter" : "Enter",
"All boards" : "Alle borden",
"Archived boards" : "Gearchiveerde borden",
"Shared with you" : "Deelde met jou",

View File

@@ -264,7 +264,6 @@
"{count} comments, {unread} unread" : "{count} reacties, {unread} ongelezen",
"Todo items" : "Te doen onderwerpen",
"Edit card title" : "Wijzig de titel van de kaart",
"Open link" : "Open link",
"Card deleted" : "Kaart verwijderd",
"Edit title" : "Titel bewerken",
"Assign to me" : "Aan mij toewijzen",
@@ -278,7 +277,6 @@
"Shift" : "Shift",
"Ctrl" : "Ctrl",
"Search" : "Zoeken",
"Enter" : "Enter",
"All boards" : "Alle borden",
"Archived boards" : "Gearchiveerde borden",
"Shared with you" : "Deelde met jou",

2449
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -70,8 +70,8 @@
"extends @nextcloud/browserslist-config"
],
"engines": {
"node": "^22.0.0",
"npm": "^10.5.0"
"node": "^20.0.0",
"npm": "^10.0.0"
},
"devDependencies": {
"@nextcloud/babel-config": "^1.2.0",
@@ -84,6 +84,7 @@
"@vue/test-utils": "^2.4.6",
"@vue/vue2-jest": "^29.2.6",
"cypress": "^13.17.0",
"cypress-split": "^1.24.22",
"eslint-plugin-cypress": "^3.6.0",
"eslint-webpack-plugin": "^5.0.1",
"jest": "^29.7.0",

View File

@@ -8,7 +8,7 @@
errorBaseline="tests/psalm-baseline.xml"
findUnusedBaselineEntry="true"
findUnusedCode="false"
phpVersion="8.2"
phpVersion="8.1"
>
<stubs>
<file name="tests/stub.phpstub" preloadClasses="true"/>