Skip to content
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
78 changes: 78 additions & 0 deletions .github/workflows/cypress-component.yml
Original file line number Diff line number Diff line change
@@ -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
32 changes: 32 additions & 0 deletions cypress.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 12 additions & 8 deletions cypress/component/ContentReferenceWidget.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})

Expand All @@ -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
Expand All @@ -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')
Expand Down Expand Up @@ -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: {
Expand All @@ -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')
}
5 changes: 1 addition & 4 deletions cypress/styleguide/global.requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -141,13 +140,11 @@ window.OC = {
webroot: '',
}
window.OCA = {}
window.appName = 'nextcloud-vue'
window.appName = 'tables'

Vue.prototype.OC = window.OC
Vue.prototype.OCA = window.OCA

window.NextcloudVueDocs = {
tags: '<?xml version="1.0"?><d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns"><d:response><d:href>/remote.php/dav/systemtags/</d:href><d:propstat><d:prop><oc:id/><oc:display-name/><oc:user-visible/><oc:user-assignable/><oc:can-assign/></d:prop><d:status>HTTP/1.1 404 Not Found</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/7</d:href><d:propstat><d:prop><oc:id>7</oc:id><oc:display-name>tag1</oc:display-name><oc:user-visible>true</oc:user-visible><oc:user-assignable>true</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/2</d:href><d:propstat><d:prop><oc:id>2</oc:id><oc:display-name>tag2</oc:display-name><oc:user-visible>false</oc:user-visible><oc:user-assignable>true</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/3</d:href><d:propstat><d:prop><oc:id>3</oc:id><oc:display-name>tag3</oc:display-name><oc:user-visible>true</oc:user-visible><oc:user-assignable>true</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/4</d:href><d:propstat><d:prop><oc:id>4</oc:id><oc:display-name>important</oc:display-name><oc:user-visible>true</oc:user-visible><oc:user-assignable>true</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/1</d:href><d:propstat><d:prop><oc:id>1</oc:id><oc:display-name>secret</oc:display-name><oc:user-visible>true</oc:user-visible><oc:user-assignable>false</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/5</d:href><d:propstat><d:prop><oc:id>5</oc:id><oc:display-name>test</oc:display-name><oc:user-visible>true</oc:user-visible><oc:user-assignable>false</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response><d:response><d:href>/remote.php/dav/systemtags/6</d:href><d:propstat><d:prop><oc:id>6</oc:id><oc:display-name>test2</oc:display-name><oc:user-visible>false</oc:user-visible><oc:user-assignable>false</oc:user-assignable><oc:can-assign>true</oc:can-assign></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response></d:multistatus>',
}

Vue.directive('tooltip', VTooltip)
12 changes: 7 additions & 5 deletions cypress/support/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
*/
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'
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: {},
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/views/ContentReferenceWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<div v-if="richObject" class="tables-content-widget">
<div v-if="richObject" class="tables-content-widget" data-cy="contentReferenceWidget">
<div class="header">
<h2>
<NcLoadingIcon v-if="!rows || rows.length === 0" :size="30" />
Expand Down
Loading