Skip to content

Commit 968280a

Browse files
committed
test: setup component tests
Adapts https://playwright.dev/docs/test-components for Vue 2. But put stories next to tests and not component sources. This follows https://github.com/nextcloud-libraries/nextcloud-vue. Assisted-by: Claude:claude-opus-5 Signed-off-by: Oleksandr Dzhychko <hey@oleks.dev>
1 parent b0c4b37 commit 968280a

16 files changed

Lines changed: 2678 additions & 4 deletions

File tree

.github/workflows/playwright.yml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# This workflow is provided via the organization template repository
2+
#
3+
# https://github.com/nextcloud-libraries/.github
4+
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
5+
#
6+
# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
# SPDX-License-Identifier: MIT
8+
9+
name: Playwright Tests
10+
11+
on:
12+
push:
13+
branches: [ main ]
14+
pull_request:
15+
branches: [ main ]
16+
17+
permissions:
18+
contents: read
19+
20+
jobs:
21+
playwright-tests:
22+
timeout-minutes: 60
23+
runs-on: ubuntu-latest
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
shardIndex: [1, 2]
28+
shardTotal: [2]
29+
outputs:
30+
node-version: ${{ steps.versions.outputs.node-version }}
31+
package-manager-version: ${{ steps.versions.outputs.package-manager-version }}
32+
33+
steps:
34+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35+
with:
36+
persist-credentials: false
37+
38+
- name: Read package.json
39+
uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0
40+
id: versions
41+
42+
- name: Set up node
43+
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
44+
with:
45+
node-version: ${{ steps.versions.outputs.node-version }}
46+
47+
- name: Set up npm
48+
run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}'
49+
50+
- name: Install dependencies
51+
run: npm ci
52+
53+
- name: Install Playwright browsers
54+
run: npx playwright install --with-deps
55+
56+
- name: Run Playwright tests
57+
run: npm run test:component -- --shard='${{ matrix.shardIndex }}/${{ matrix.shardTotal }}'
58+
59+
- name: Upload blob report to GitHub Actions Artifacts
60+
if: ${{ !cancelled() }}
61+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
62+
with:
63+
name: blob-report-${{ matrix.shardIndex }}
64+
path: blob-report
65+
retention-days: 1
66+
67+
merge-reports:
68+
# Merge reports after playwright-tests, even if some shards have failed
69+
if: ${{ !cancelled() }}
70+
needs: [playwright-tests]
71+
72+
runs-on: ubuntu-latest
73+
steps:
74+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
75+
with:
76+
persist-credentials: false
77+
78+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
79+
with:
80+
node-version: ${{ needs.playwright-tests.outputs.node-version }}
81+
82+
- name: Set up npm
83+
run: npm i -g 'npm@${{ needs.playwright-tests.outputs.package-manager-version }}'
84+
85+
- name: Install dependencies
86+
run: npm ci
87+
88+
- name: Download blob reports from GitHub Actions Artifacts
89+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
90+
with:
91+
path: all-blob-reports
92+
pattern: blob-report-*
93+
merge-multiple: true
94+
95+
- name: Merge into HTML Report
96+
run: npx playwright merge-reports --reporter html,github ./all-blob-reports
97+
98+
- name: Upload HTML report
99+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
100+
with:
101+
name: html-report--attempt-${{ github.run_attempt }}
102+
path: playwright-report
103+
retention-days: 7
104+
105+
- name: Show the logs
106+
run: |
107+
echo 'To view the report:'
108+
echo ' 1. Extract the folder from the zip file'
109+
echo ' 2. run "npx playwright show-report name-of-my-extracted-playwright-report"'
110+
111+
summary:
112+
permissions:
113+
contents: none
114+
runs-on: ubuntu-latest
115+
needs: [playwright-tests]
116+
117+
if: always()
118+
119+
name: playwright-test-summary
120+
121+
steps:
122+
- name: Summary status
123+
run: if ${{ needs.playwright-tests.result != 'success' }}; then exit 1; fi
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
2+
# SPDX-License-Identifier: MIT
3+
4+
# Adapted from https://github.com/nextcloud-libraries/nextcloud-vue/blob/ac7e60606aee3d38f51b5385beb2f4e61a94c0ed/.github/workflows/server-styling-update.yml
5+
6+
name: Update server styling
7+
8+
9+
on:
10+
workflow_dispatch:
11+
schedule:
12+
- cron: "0 5 * * *"
13+
14+
jobs:
15+
server-styling-update:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
branch: ['main', 'stable8']
20+
21+
steps:
22+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23+
with:
24+
persist-credentials: false
25+
ref: ${{ matrix.branch }}
26+
27+
- name: Download css
28+
run: |
29+
cd src/tests/component/gallery/assets/server
30+
curl https://nextcloud.github.io/server/apps/theming/css/default.css > apps/theming/css/default.css
31+
curl https://nextcloud.github.io/server/core/css/server.css | sed 's|/\*# sourceMappingURL=[^*]*\*/||' > core/css/server.css
32+
curl https://nextcloud.github.io/server/dist/icons.css > dist/icons.css
33+
34+
- name: Create Pull Request
35+
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
36+
with:
37+
token: ${{ secrets.COMMAND_BOT_PAT }}
38+
author: nextcloud-command <nextcloud-command@users.noreply.github.com>
39+
committer: nextcloud-command <nextcloud-command@users.noreply.github.com>
40+
commit-message: "chore(docs): update docs styling from server"
41+
signoff: true
42+
branch: 'automated/${{ github.run_id }}/server-styling-update-${{ matrix.branch }}'
43+
title: '[${{ matrix.branch }}] chore(docs): Automated styling update from server'
44+
add-paths: styleguide/assets
45+
body: |
46+
Auto-generated update of our locally cached css styling from
47+
* https://nextcloud.github.io/server/apps/theming/css/default.css
48+
* https://nextcloud.github.io/server/core/css/server.css
49+
* https://nextcloud.github.io/server/dist/icons.css
50+
labels: |
51+
dependencies
52+
3. to review

REUSE.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ precedence = "aggregate"
8989
SPDX-FileCopyrightText = "2021-2024 Nextcloud GmbH and Nextcloud contributors"
9090
SPDX-License-Identifier = "AGPL-3.0-or-later"
9191

92+
[[annotations]]
93+
path = ["src/tests/component/snapshots/**", "src/tests/component/gallery/assets/server/dist/icons.css"]
94+
precedence = "aggregate"
95+
SPDX-FileCopyrightText = "Nextcloud GmbH and Nextcloud contributors"
96+
SPDX-License-Identifier = "AGPL-3.0-or-later"
97+
9298
[[annotations]]
9399
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"]
94100
precedence = "aggregate"

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"lint:fix": "eslint --ext .js,.ts,.vue --fix .",
1414
"stylelint": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\"",
1515
"stylelint:fix": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\" --fix",
16+
"test:component": "playwright test --config playwright-ct.config.ts",
17+
"test:component:gui": "playwright test --config playwright-ct.config.ts --ui",
1618
"test:e2e": "playwright test",
1719
"test:e2e:ui": "playwright test --ui",
1820
"test:unit": "vitest --run",

playwright-ct.config.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { defineConfig, devices } from '@playwright/test'
7+
8+
const GALLERY_URL = 'http://localhost:5173/src/tests/component/gallery/index.html'
9+
10+
export default defineConfig({
11+
testDir: 'src/tests/component',
12+
snapshotDir: 'src/tests/component/snapshots',
13+
forbidOnly: !!process.env.CI,
14+
fullyParallel: true,
15+
workers: process.env.CI ? 1 : undefined,
16+
reporter: process.env.CI ? 'blob' : 'html',
17+
18+
use: {
19+
...devices['Desktop Chrome'],
20+
baseURL: GALLERY_URL,
21+
serviceWorkers: 'block',
22+
reuseContext: true,
23+
trace: 'retain-on-failure',
24+
},
25+
webServer: {
26+
command: 'npx vite --config vite-ct.config.ts',
27+
url: GALLERY_URL,
28+
reuseExistingServer: !process.env.CI,
29+
},
30+
})

src/service/caldavService.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

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

1010
let client = null
@@ -60,7 +60,7 @@ export function getCurrentUserPrincipal() {
6060
/**
6161
* Fetch all calendars from the server
6262
*
63-
* @return {Promise<CalendarHome>}
63+
* @return {Promise<import('@nextcloud/cdav-library')CalendarHome>}
6464
*/
6565
export function getCalendarHome() {
6666
return getClient().calendarHomes[0]
@@ -69,7 +69,7 @@ export function getCalendarHome() {
6969
/**
7070
* Fetch all collections in the calendar home from the server
7171
*
72-
* @return {Promise<Collection[]>}
72+
* @return {Promise<import('@nextcloud/cdav-library').Collection[]>}
7373
*/
7474
export async function findAll() {
7575
return await getCalendarHome().findAllCalDAVCollectionsGrouped()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import type { OneSender } from './NewMessageModal.story.ts'
7+
8+
import { expect, test } from '@playwright/test'
9+
10+
test('shows the only account as sender', async ({ page, mount }) => {
11+
const component = await mount<typeof OneSender>('components/NewMessageModal/OneSender')
12+
13+
const fromInput = component.getByRole('combobox', { name: 'Select account' }).locator('..')
14+
await expect(fromInput).toContainText('Jane Doe <jane@example.org>')
15+
await expect(page).toHaveScreenshot()
16+
})
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import NewMessageModal from '../../../components/NewMessageModal.vue'
7+
import { EDITOR_MODE_TEXT } from '../../../store/constants.js'
8+
import useMainStore from '../../../store/mainStore.js'
9+
10+
const account = {
11+
id: 1,
12+
isUnified: false,
13+
name: 'Jane Doe',
14+
emailAddress: 'jane@example.org',
15+
editorMode: EDITOR_MODE_TEXT,
16+
signature: '',
17+
signatureAboveQuote: false,
18+
smimeCertificateId: null,
19+
connectionStatus: true,
20+
aliases: [],
21+
}
22+
23+
export const OneSender = {
24+
created() {
25+
const store = useMainStore()
26+
27+
store.$patch({
28+
textBlocksFetched: true,
29+
myTextBlocks: [],
30+
sharedTextBlocks: [],
31+
smimeCertificates: [],
32+
preferences: { 'reply-mode': 'top' },
33+
accountsUnmapped: { [account.id]: account },
34+
accountList: [account.id],
35+
})
36+
37+
// Sets newMessage, composerSessionId and showMessageComposer together.
38+
store.startComposerSessionMutation({
39+
type: 'imap',
40+
data: {
41+
accountId: account.id,
42+
aliasId: null,
43+
to: [],
44+
cc: [],
45+
bcc: [],
46+
subject: '',
47+
attachments: [],
48+
bodyPlain: '',
49+
isHtml: false,
50+
sendAt: 0,
51+
},
52+
forwardedMessages: [],
53+
})
54+
},
55+
56+
render(h) {
57+
return h('div', [
58+
h(NewMessageModal, { props: { accounts: [account] } }),
59+
])
60+
},
61+
}

0 commit comments

Comments
 (0)