diff --git a/.github/workflows/cypress-component.yml b/.github/workflows/cypress-component.yml
new file mode 100644
index 0000000000..e16ee12916
--- /dev/null
+++ b/.github/workflows/cypress-component.yml
@@ -0,0 +1,78 @@
+# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: MIT
+
+name: Cypress Component Tests
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - master
+ - stable*
+
+concurrency:
+ group: cypress-component-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ APP_NAME: tables
+
+jobs:
+ component-tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: [20]
+
+ steps:
+ - name: Checkout app
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: 'npm'
+ cache-dependency-path: package-lock.json
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build app
+ run: npm run build --if-present
+
+ - name: Run component tests
+ run: npm run tests:component
+
+ - name: Upload test failure screenshots
+ uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: cypress-component-screenshots-node${{ matrix.node-version }}
+ path: cypress/screenshots/
+ retention-days: 5
+
+ - name: Upload test videos
+ uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: cypress-component-videos-node${{ matrix.node-version }}
+ path: cypress/videos/
+ retention-days: 5
+
+ summary:
+ permissions:
+ contents: none
+ runs-on: ubuntu-latest-low
+ needs: component-tests
+
+ if: always()
+
+ name: cypress-component-summary
+
+ steps:
+ - name: Summary status
+ run: if ${{ needs.component-tests.result != 'success' && needs.component-tests.result != 'skipped' }}; then exit 1; fi
diff --git a/cypress.config.mjs b/cypress.config.mjs
index 1065fc6bb5..889089b86d 100644
--- a/cypress.config.mjs
+++ b/cypress.config.mjs
@@ -45,6 +45,38 @@ export default defineConfig({
devServer: {
framework: 'vue',
bundler: 'vite',
+ viteConfig: {
+ plugins: [vue(), nodePolyfills()],
+ optimizeDeps: {
+ exclude: [
+ 'vite-plugin-node-polyfills/shims/buffer',
+ 'vite-plugin-node-polyfills/shims/global',
+ 'vite-plugin-node-polyfills/shims/process'
+ ],
+ force: true
+ },
+ define: {
+ global: 'globalThis',
+ },
+ },
+ },
+ setupNodeEvents(on, config) {
+ on('file:preprocessor', vitePreprocessor({
+ plugins: [vue(), nodePolyfills()],
+ configFile: false,
+ optimizeDeps: {
+ exclude: [
+ 'vite-plugin-node-polyfills/shims/buffer',
+ 'vite-plugin-node-polyfills/shims/global',
+ 'vite-plugin-node-polyfills/shims/process'
+ ],
+ force: true
+ },
+ define: {
+ global: 'globalThis',
+ },
+ }))
+ return config
},
viewportWidth: 800,
viewportHeight: 600,
diff --git a/cypress/component/ContentReferenceWidget.cy.js b/cypress/component/ContentReferenceWidget.cy.js
index 27862b07e1..acd16c04d5 100644
--- a/cypress/component/ContentReferenceWidget.cy.js
+++ b/cypress/component/ContentReferenceWidget.cy.js
@@ -22,7 +22,7 @@ describe('ContentReferenceWidget', () => {
// Verify the table loaded the richObject
// by checking the title
- cy.get('.tables-content-widget h2').as('heading')
+ cy.get('[data-cy="contentReferenceWidget"] h2').as('heading')
cy.get('@heading').contains(title)
})
@@ -32,7 +32,7 @@ describe('ContentReferenceWidget', () => {
const searchTerm = 'cat'
// Search for the row including the above search term
- cy.get('@options').find('input').type(searchTerm)
+ cy.get('@options').find('input').first().type(searchTerm)
// Ensure there is only one resultant row and
// verify the row correctly includes the search term
@@ -47,11 +47,15 @@ describe('ContentReferenceWidget', () => {
// Load a fixture used to reply to the create row request
cy.fixture('widgets/createRow.json')
.then((rowData) => {
- cy.reply('**/index.php/apps/tables/row', rowData)
+ cy.reply('**/ocs/v2.php/apps/tables/api/2/tables/*/rows', rowData)
+
+ // Also mock the reload rows endpoint to return updated rows including the new one
+ const updatedRows = [...richObject.rows, rowData]
+ cy.reply('**/apps/tables/row/table/*', updatedRows)
})
// Click the Create Row button
- cy.get('@options').find('button').click()
+ cy.get('@options').find('button').first().click()
// Input row data
cy.get('[data-cy="Name"] input').type('Hello')
@@ -99,7 +103,7 @@ describe('ContentReferenceWidget', () => {
})
function mountContentWidget(richObject) {
- cy.reply('**/index.php/apps/tables/row/table/*', richObject.rows)
+ cy.reply('**/apps/tables/row/table/*', richObject.rows)
cy.mount(ContentReferenceWidget, {
propsData: {
@@ -108,7 +112,7 @@ function mountContentWidget(richObject) {
})
// Get some often used elements
- cy.get('.tables-content-widget > .options').as('options')
- cy.get('.tables-content-widget .NcTable table').as('table')
- cy.get('@table').find('tbody tr[data-cy="customTableRow"]').as('rows')
+ cy.get('[data-cy="contentReferenceWidget"] .options').as('options')
+ cy.get('[data-cy="contentReferenceWidget"] .NcTable table').as('table')
+ cy.get('@table').find('tr[data-cy="customTableRow"]').as('rows')
}
diff --git a/cypress/styleguide/global.requires.js b/cypress/styleguide/global.requires.js
index 553f45cba3..3ce16d67b5 100644
--- a/cypress/styleguide/global.requires.js
+++ b/cypress/styleguide/global.requires.js
@@ -5,7 +5,6 @@
/* eslint-disable-next-line */
import 'core-js/stable'
import Vue from 'vue'
-import VTooltip from './../src/directives/Tooltip/index.js'
import axios from '@nextcloud/axios'
@@ -141,7 +140,7 @@ window.OC = {
webroot: '',
}
window.OCA = {}
-window.appName = 'nextcloud-vue'
+window.appName = 'tables'
Vue.prototype.OC = window.OC
Vue.prototype.OCA = window.OCA
@@ -149,5 +148,3 @@ Vue.prototype.OCA = window.OCA
window.NextcloudVueDocs = {
tags: '/remote.php/dav/systemtags/HTTP/1.1 404 Not Found/remote.php/dav/systemtags/77tag1truetruetrueHTTP/1.1 200 OK/remote.php/dav/systemtags/22tag2falsetruetrueHTTP/1.1 200 OK/remote.php/dav/systemtags/33tag3truetruetrueHTTP/1.1 200 OK/remote.php/dav/systemtags/44importanttruetruetrueHTTP/1.1 200 OK/remote.php/dav/systemtags/11secrettruefalsetrueHTTP/1.1 200 OK/remote.php/dav/systemtags/55testtruefalsetrueHTTP/1.1 200 OK/remote.php/dav/systemtags/66test2falsefalsetrueHTTP/1.1 200 OK',
}
-
-Vue.directive('tooltip', VTooltip)
diff --git a/cypress/support/component.js b/cypress/support/component.js
index 55cb0dc519..4f179fd968 100644
--- a/cypress/support/component.js
+++ b/cypress/support/component.js
@@ -4,8 +4,12 @@
*/
import { mount } from 'cypress/vue2'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
-import store from '../../src/store/store.js'
-import data from '../../src/store/data.js'
+import { createPinia, PiniaVuePlugin } from 'pinia'
+import Vue from 'vue'
+Vue.use(PiniaVuePlugin)
+const pinia = createPinia()
+
+import '../styleguide/global.requires.js'
// Styles necessary for rendering the component
import '../styleguide/assets/default.css'
@@ -13,13 +17,11 @@ import '../styleguide/assets/additional.css'
import '../styleguide/assets/icons.css'
const prepareOptions = (options = {}) => {
- store.data = data
-
const defaultOptions = {
+ pinia,
extensions: {
mixins: [
{ methods: { t, n } },
- { store, },
],
plugins: [],
components: {},
diff --git a/package.json b/package.json
index d46d52890b..01e3bdd60d 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,9 @@
"lint": "eslint --ext .js,.vue src",
"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"
+ "stylelint:fix": "stylelint 'css/*.css' 'css/*.scss' 'src/**/*.scss' 'src/**/*.vue' --fix",
+ "tests": "npx cypress run",
+ "tests:component": "npx cypress run --component"
},
"dependencies": {
"@mdi/svg": "^7.4.47",
diff --git a/src/views/ContentReferenceWidget.vue b/src/views/ContentReferenceWidget.vue
index 64c20b9b5d..30e9e5ee69 100644
--- a/src/views/ContentReferenceWidget.vue
+++ b/src/views/ContentReferenceWidget.vue
@@ -3,7 +3,7 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
-