Skip to content
Closed
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
123 changes: 123 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud-libraries/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

name: Playwright Tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate run only for fronted changes.
See #13586

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe even wrap in to the workflow that runs the node tests. Also simplify, because we do not need parllel running.


on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

permissions:
contents: read

jobs:
playwright-tests:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2]
shardTotal: [2]
outputs:
node-version: ${{ steps.versions.outputs.node-version }}
package-manager-version: ${{ steps.versions.outputs.package-manager-version }}

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Read package.json
uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0
id: versions

- name: Set up node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ steps.versions.outputs.node-version }}

- name: Set up npm
run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}'

- name: Install dependencies
run: npm ci

- name: Install Playwright browsers
run: npx playwright install --with-deps

- name: Run Playwright tests
run: npm run test:component -- --shard='${{ matrix.shardIndex }}/${{ matrix.shardTotal }}'

- name: Upload blob report to GitHub Actions Artifacts
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
retention-days: 1

merge-reports:
# Merge reports after playwright-tests, even if some shards have failed
if: ${{ !cancelled() }}
needs: [playwright-tests]

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ needs.playwright-tests.outputs.node-version }}

- name: Set up npm
run: npm i -g 'npm@${{ needs.playwright-tests.outputs.package-manager-version }}'

- name: Install dependencies
run: npm ci

- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true

- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html,github ./all-blob-reports

- name: Upload HTML report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: html-report--attempt-${{ github.run_attempt }}
path: playwright-report
retention-days: 7

- name: Show the logs
run: |
echo 'To view the report:'
echo ' 1. Extract the folder from the zip file'
echo ' 2. run "npx playwright show-report name-of-my-extracted-playwright-report"'

summary:
permissions:
contents: none
runs-on: ubuntu-latest
needs: [playwright-tests]

if: always()

name: playwright-test-summary

steps:
- name: Summary status
run: if ${{ needs.playwright-tests.result != 'success' }}; then exit 1; fi
52 changes: 52 additions & 0 deletions .github/workflows/server-styling-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

# Adapted from https://github.com/nextcloud-libraries/nextcloud-vue/blob/ac7e60606aee3d38f51b5385beb2f4e61a94c0ed/.github/workflows/server-styling-update.yml

name: Update server styling


on:
workflow_dispatch:
schedule:
- cron: "0 5 * * *"

jobs:
server-styling-update:
runs-on: ubuntu-latest
strategy:
matrix:
branch: ['main', 'stable8']

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use correct branches


steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ matrix.branch }}

- name: Download css
run: |
cd src/tests/component/gallery/assets/server
curl https://nextcloud.github.io/server/apps/theming/css/default.css > apps/theming/css/default.css

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference is to commit the server style like https://github.com/nextcloud-libraries/nextcloud-vue/blob/main/.github/workflows/server-styling-update.yml

Pros
  • It works offline
  • It's clear which styles is tested with
  • Can run npm run test:component without needing the checkout of sever
    • Simplifies CI running
    • Gives more flexibility in local development (e.g. fixing visual issue or creating mockups without server running)
Alternatives

Not sure how you develop with this repo, let me know if you want to keep it as proposed or do it differently.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@susnux I want to apply component testing including visual regression testing to a groupware apps. Do you have thoughts of a good long term solution for making server styles available for that?

I thought of creating an NPM package nextcloud/testing (repo nextcloud-libraries/nextcloud-testing that bundles styling. NPM package would automatically be published for server versions and updated through renovate in using repos.

<head>
	<link href="node_modules/nextcloud/testing/styles/server/stable35/apps/theming/css/default.css" rel="stylesheet" />
	<link href="node_modules/nextcloud/testing/styles/server/stable35/core/css/server.css" rel="stylesheet" />
</head>

It would have /server/stable35/, /server/stable34, /server/master etc. for matrix testing.

This would remove CI complexity and maintance for this from individual apps (mail, calendar, contacts) and libraries (nextcloud-vue).

curl https://nextcloud.github.io/server/core/css/server.css | sed 's|/\*# sourceMappingURL=[^*]*\*/||' > core/css/server.css
curl https://nextcloud.github.io/server/dist/icons.css > dist/icons.css

- name: Create Pull Request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.COMMAND_BOT_PAT }}
author: nextcloud-command <nextcloud-command@users.noreply.github.com>
committer: nextcloud-command <nextcloud-command@users.noreply.github.com>
commit-message: "chore(docs): update docs styling from server"
signoff: true
branch: 'automated/${{ github.run_id }}/server-styling-update-${{ matrix.branch }}'
title: '[${{ matrix.branch }}] chore(docs): Automated styling update from server'
add-paths: styleguide/assets
body: |
Auto-generated update of our locally cached css styling from
* https://nextcloud.github.io/server/apps/theming/css/default.css
* https://nextcloud.github.io/server/core/css/server.css
* https://nextcloud.github.io/server/dist/icons.css
labels: |
dependencies
3. to review
6 changes: 6 additions & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ precedence = "aggregate"
SPDX-FileCopyrightText = "2021-2024 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"

[[annotations]]
path = ["src/tests/component/snapshots/**", "src/tests/component/gallery/assets/server/dist/icons.css"]
precedence = "aggregate"
SPDX-FileCopyrightText = "Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"

[[annotations]]
path = ["src/tests/data/.editorconfig", "tests/data/mime-html.txt", "tests/data/mime-html-greek.txt", "tests/data/mime-html-image.txt", "tests/data/mime-text.txt", "tests/data/test.txt"]
precedence = "aggregate"
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"lint:fix": "eslint --ext .js,.ts,.vue --fix .",
"stylelint": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\"",
"stylelint:fix": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\" --fix",
"test:component": "playwright test --config playwright-ct.config.ts",
"test:component:gui": "playwright test --config playwright-ct.config.ts --ui",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:unit": "vitest --run",
Expand Down
30 changes: 30 additions & 0 deletions playwright-ct.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { defineConfig, devices } from '@playwright/test'

const GALLERY_URL = 'http://localhost:5173/src/tests/component/gallery/index.html'

export default defineConfig({
testDir: 'src/tests/component',
snapshotDir: 'src/tests/component/snapshots',
forbidOnly: !!process.env.CI,
fullyParallel: true,
workers: process.env.CI ? 1 : undefined,
reporter: process.env.CI ? 'blob' : 'html',

use: {
...devices['Desktop Chrome'],
baseURL: GALLERY_URL,
serviceWorkers: 'block',
reuseContext: true,
trace: 'retain-on-failure',
},
webServer: {
command: 'npx vite --config vite-ct.config.ts',
url: GALLERY_URL,
reuseExistingServer: !process.env.CI,
},
})
6 changes: 3 additions & 3 deletions src/service/caldavService.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { getRequestToken } from '@nextcloud/auth'
import DavClient, { CalendarHome, Collection, Principal } from '@nextcloud/cdav-library'
import DavClient from '@nextcloud/cdav-library'
import { generateRemoteUrl } from '@nextcloud/router'

let client = null
Expand Down Expand Up @@ -60,7 +60,7 @@ export function getCurrentUserPrincipal() {
/**
* Fetch all calendars from the server
*
* @return {Promise<CalendarHome>}
* @return {Promise<import('@nextcloud/cdav-library')CalendarHome>}
*/
export function getCalendarHome() {
return getClient().calendarHomes[0]
Expand All @@ -69,7 +69,7 @@ export function getCalendarHome() {
/**
* Fetch all collections in the calendar home from the server
*
* @return {Promise<Collection[]>}
* @return {Promise<import('@nextcloud/cdav-library').Collection[]>}
*/
export async function findAll() {
return await getCalendarHome().findAllCalDAVCollectionsGrouped()
Expand Down
16 changes: 16 additions & 0 deletions src/tests/component/components/NewMessageModal.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { OneSender } from './NewMessageModal.story.ts'

import { expect, test } from '@playwright/test'

test('shows the only account as sender', async ({ page, mount }) => {
const component = await mount<typeof OneSender>('components/NewMessageModal/OneSender')

const fromInput = component.getByRole('combobox', { name: 'Select account' }).locator('..')
await expect(fromInput).toContainText('Jane Doe <jane@example.org>')
await expect(page).toHaveScreenshot()
})
61 changes: 61 additions & 0 deletions src/tests/component/components/NewMessageModal.story.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import NewMessageModal from '../../../components/NewMessageModal.vue'
import { EDITOR_MODE_TEXT } from '../../../store/constants.js'
import useMainStore from '../../../store/mainStore.js'

const account = {
id: 1,
isUnified: false,
name: 'Jane Doe',
emailAddress: 'jane@example.org',
editorMode: EDITOR_MODE_TEXT,
signature: '',
signatureAboveQuote: false,
smimeCertificateId: null,
connectionStatus: true,
aliases: [],
}

export const OneSender = {
created() {
const store = useMainStore()

store.$patch({
textBlocksFetched: true,
myTextBlocks: [],
sharedTextBlocks: [],
smimeCertificates: [],
preferences: { 'reply-mode': 'top' },
accountsUnmapped: { [account.id]: account },
accountList: [account.id],
})

// Sets newMessage, composerSessionId and showMessageComposer together.
store.startComposerSessionMutation({
type: 'imap',
data: {
accountId: account.id,
aliasId: null,
to: [],
cc: [],
bcc: [],
subject: '',
attachments: [],
bodyPlain: '',
isHtml: false,
sendAt: 0,
},
forwardedMessages: [],
})
},

render(h) {
return h('div', [
h(NewMessageModal, { props: { accounts: [account] } }),
])
},
}
Loading
Loading