Skip to content

Commit 8fc17a5

Browse files
mejo-backportbot[bot]
authored andcommitted
test(playwright): migrate propfind tests from Cypress to Playwright
Signed-off-by: Jonas <jonas@freesources.org> Assisted-by: OpenCode:claude-fable-5
1 parent e3197ff commit 8fc17a5

7 files changed

Lines changed: 290 additions & 184 deletions

File tree

cypress/e2e/propfind.spec.js

Lines changed: 0 additions & 114 deletions
This file was deleted.

cypress/support/commands.js

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -243,76 +243,6 @@ Cypress.Commands.add('getFileContent', (path) => {
243243
.then((response) => response.data)
244244
})
245245

246-
Cypress.Commands.add('propfindFolder', (path, depth = 0, properties = null) => {
247-
const defaultProperties = `
248-
<nc:rich-workspace />
249-
<nc:rich-workspace-file />`
250-
251-
const propsXml = properties
252-
? properties.map((p) => `<${p} />`).join('\n')
253-
: defaultProperties
254-
255-
const rootPath = `${url}/remote.php/webdav/`
256-
const requestPath = path === '/' ? rootPath : `${rootPath}${path}`
257-
258-
return axios
259-
.request({
260-
method: 'PROPFIND',
261-
url: requestPath,
262-
headers: {
263-
Depth: depth,
264-
'Content-Type': 'application/xml',
265-
},
266-
data: `<?xml version="1.0"?>
267-
<d:propfind xmlns:d="DAV:"
268-
xmlns:oc="http://owncloud.org/ns"
269-
xmlns:nc="http://nextcloud.org/ns">
270-
<d:prop>
271-
${propsXml}
272-
</d:prop>
273-
</d:propfind>`,
274-
})
275-
.then((response) => {
276-
const parser = new DOMParser()
277-
const xmlDoc = parser.parseFromString(response.data, 'text/xml')
278-
const responses = xmlDoc.querySelectorAll('d\\:response, response')
279-
const results = Array.from(responses).map((resp) => {
280-
const props = {}
281-
const propStats = resp.querySelectorAll('d\\:propstat, propstat')
282-
propStats.forEach((propStat) => {
283-
const status =
284-
propStat.querySelector('d\\:status, status')?.textContent
285-
286-
// Skip properties with 404 status ( not found)
287-
if (status?.includes('404')) {
288-
return
289-
}
290-
291-
const propElements = resp.querySelectorAll(
292-
'd\\:prop > *, prop > * ',
293-
)
294-
295-
propElements.forEach((prop) => {
296-
const tagName = prop.localName
297-
const namespace = prop.namespaceURI
298-
299-
let key = tagName
300-
if (namespace === 'http://nextcloud.org/ns') {
301-
key = `nc:${tagName}`
302-
} else if (namespace === 'http://owncloud.org/ns') {
303-
key = `oc:${tagName}`
304-
}
305-
306-
props[key] = prop.textContent || ''
307-
})
308-
})
309-
return props
310-
})
311-
312-
return depth > 0 ? results : results[0] || {}
313-
})
314-
})
315-
316246
Cypress.Commands.add('reloadFileList', () => {
317247
cy.get('[title="Reload current directory"] button').click()
318248
return cy.get('button').contains('Reload content').click()

package-lock.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
"@nextcloud/prettier-config": "^1.2.0",
114114
"@nextcloud/vite-config": "^1.7.2",
115115
"@playwright/test": "^1.60.0",
116+
"@types/jsdom": "^30.0.0",
116117
"@types/markdown-it": "^14.1.2",
117118
"@types/node": "^26.2.0",
118119
"@vitejs/plugin-vue2": "^2.3.4",

playwright/e2e/propfind.spec.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { expect } from '@playwright/test'
7+
import { createFolder, uploadFile } from '../support/fixtures/Node.ts'
8+
import { test } from '../support/fixtures/random-user.ts'
9+
import { setTextSetting } from '../support/fixtures/settings.ts'
10+
import {
11+
deleteWebDAVResource,
12+
PROPERTY_WORKSPACE,
13+
PROPERTY_WORKSPACE_FILE,
14+
PROPERTY_WORKSPACE_FILE_FLAT,
15+
PROPERTY_WORKSPACE_FLAT,
16+
propfindFolder,
17+
} from '../support/fixtures/webdav.ts'
18+
19+
test.describe('Text PROPFIND extension', () => {
20+
test.describe('with workspaces enabled', () => {
21+
test.beforeEach(async ({ user }) => {
22+
await setTextSetting(user, 'workspace_enabled', 1)
23+
})
24+
25+
test('always adds rich workspace property', async ({ page, user }) => {
26+
const properties = [PROPERTY_WORKSPACE_FLAT, PROPERTY_WORKSPACE_FILE_FLAT]
27+
28+
await page.goto('/apps/dashboard')
29+
await user.uploadFile({ name: 'Readme.md', content: '' })
30+
31+
const [root1] = await propfindFolder(user, '/', 0, properties)
32+
expect(root1).toHaveProperty(PROPERTY_WORKSPACE_FLAT, '')
33+
34+
await deleteWebDAVResource(user, '/Readme.md')
35+
await user.uploadFile({ name: 'Readme.md', content: '## Hello world\n' })
36+
const [root2] = await propfindFolder(user, '/', 0, properties)
37+
expect(root2).toHaveProperty(PROPERTY_WORKSPACE_FLAT, '## Hello world\n')
38+
39+
await deleteWebDAVResource(user, '/Readme.md')
40+
const [root3] = await propfindFolder(user, '/', 0, properties)
41+
expect(root3).toHaveProperty(PROPERTY_WORKSPACE_FLAT, '')
42+
})
43+
44+
test('never adds rich workspace property to nested folders for flat properties', async ({
45+
page,
46+
user,
47+
}) => {
48+
const properties = [
49+
PROPERTY_WORKSPACE_FLAT,
50+
PROPERTY_WORKSPACE_FILE_FLAT,
51+
]
52+
53+
await page.goto('/apps/dashboard')
54+
await createFolder({ name: 'workspace-flat', owner: user })
55+
56+
const results1 = await propfindFolder(user, '/', 1, properties)
57+
const folder1 = results1.find((r) =>
58+
r['d:href']?.endsWith('/workspace-flat/'),
59+
)
60+
expect(folder1).toHaveProperty(PROPERTY_WORKSPACE_FLAT, '')
61+
62+
await uploadFile({
63+
name: 'workspace-flat/Readme.md',
64+
content: '## Hello world\n',
65+
owner: user,
66+
})
67+
const results2 = await propfindFolder(user, '/', 1, properties)
68+
const folder2 = results2.find((r) =>
69+
r['d:href']?.endsWith('/workspace-flat/'),
70+
)
71+
expect(folder2).toHaveProperty(PROPERTY_WORKSPACE_FLAT, '')
72+
})
73+
74+
// Android app relies on this to detect rich workspace availability in subfolders properly
75+
test('adds rich workspace property to nested folders for the default properties', async ({
76+
page,
77+
user,
78+
}) => {
79+
const properties = [PROPERTY_WORKSPACE, PROPERTY_WORKSPACE_FILE]
80+
81+
await page.goto('/apps/dashboard')
82+
await createFolder({ name: 'workspace', owner: user })
83+
84+
const results1 = await propfindFolder(user, '/', 1, properties)
85+
const folder1 = results1.find((r) =>
86+
r['d:href']?.endsWith('/workspace/'),
87+
)
88+
expect(folder1).toHaveProperty(PROPERTY_WORKSPACE, '')
89+
90+
await uploadFile({
91+
name: 'workspace/Readme.md',
92+
content: '## Hello world\n',
93+
owner: user,
94+
})
95+
const results2 = await propfindFolder(user, '/', 1, properties)
96+
const folder2 = results2.find((r) =>
97+
r['d:href']?.endsWith('/workspace/'),
98+
)
99+
expect(folder2).toHaveProperty(PROPERTY_WORKSPACE, '## Hello world\n')
100+
})
101+
})
102+
103+
test.describe('with workspaces disabled', () => {
104+
test.beforeEach(async ({ user }) => {
105+
await setTextSetting(user, 'workspace_enabled', 0)
106+
})
107+
108+
test('does not return a rich workspace property', async ({ page, user }) => {
109+
await page.goto('/apps/dashboard')
110+
111+
const results1 = await propfindFolder(user, '/', 1, [
112+
PROPERTY_WORKSPACE_FLAT,
113+
PROPERTY_WORKSPACE_FILE_FLAT,
114+
])
115+
for (const result of results1) {
116+
expect(result).not.toHaveProperty(PROPERTY_WORKSPACE_FLAT)
117+
}
118+
119+
await user.uploadFile({ name: 'Readme.md', content: '## Hello world\n' })
120+
const results2 = await propfindFolder(user, '/', 1, [
121+
PROPERTY_WORKSPACE_FLAT,
122+
PROPERTY_WORKSPACE_FILE_FLAT,
123+
])
124+
for (const result of results2) {
125+
expect(result).not.toHaveProperty(PROPERTY_WORKSPACE_FLAT)
126+
}
127+
128+
await createFolder({ name: 'without-workspace', owner: user })
129+
const results3 = await propfindFolder(user, '/', 1)
130+
const folder = results3.find((r) =>
131+
r['d:href']?.endsWith('/without-workspace/'),
132+
)
133+
expect(folder).not.toHaveProperty(PROPERTY_WORKSPACE)
134+
})
135+
})
136+
})

0 commit comments

Comments
 (0)