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
6 changes: 6 additions & 0 deletions tests/playwright/e2e/appstore/admin-settings-apps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ test.describe('Settings: App management', () => {
await appstorePage.appsTable().waitFor({ state: 'visible', timeout: 10000 })
})

test.afterAll(async () => {
// Leave the apps the tests toggle in the state the other suites expect
await runOcc(['app:disable', 'testing'], { failOnError: false })
await runOcc(['app:enable', 'updatenotification'], { failOnError: false })
})

test('Can enable an installed app', async ({ page, appstorePage }) => {
// Intercept the enable app request
const enableRequest = page.waitForResponse((response) => response.url().includes('/ocs/v2.php/apps/appstore/api/v1/apps/enable'))
Expand Down
8 changes: 5 additions & 3 deletions tests/playwright/e2e/files/files-renaming.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ test.describe('Files: Rename nodes', () => {
const input = filesListPage.getRenameInputForFile('file.txt')
await expect(input).toBeVisible()

const { selectionStart, selectionEnd } = await input.evaluate((el) => ({ selectionStart: (el as HTMLInputElement).selectionStart, selectionEnd: (el as HTMLInputElement).selectionEnd }))
expect(selectionStart).toBe(0)
expect(selectionEnd).toBe('file'.length)
// The selection is applied after the input is shown
await expect.poll(() => input.evaluate((el) => {
const { selectionStart, selectionEnd } = el as HTMLInputElement
return { selectionStart, selectionEnd }
})).toEqual({ selectionStart: 0, selectionEnd: 'file'.length })
})

test('shows validation error on invalid filename', async ({ filesListPage }) => {
Expand Down
3 changes: 2 additions & 1 deletion tests/playwright/e2e/files_trashbin/files.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { TrashbinListPage } from '../../support/sections/TrashbinListPage.t
import { readFile } from 'node:fs/promises'
import { expect, test } from '../../support/fixtures/files-trashbin-page.ts'
import { mkdir, rm, uploadContent } from '../../support/utils/dav.ts'
import { createShare, ShareType } from '../../support/utils/sharing.ts'
import { createShare, ShareType, waitForShare } from '../../support/utils/sharing.ts'
import { setUserDisplayName } from '../../support/utils/users.ts'

test.describe('files_trashbin: download files', () => {
Expand Down Expand Up @@ -71,6 +71,7 @@ test.describe('files_trashbin: file row', () => {

const fileId = Number(await uploadContent(aliceRequest, user, '<content>', 'text/plain', '/Shared/test-file.txt'))
// Bob (the sharee) deletes the file from his view of the shared folder
await waitForShare(bobRequest, bob, 'Shared', 'test-file.txt')
await rm(bobRequest, bob, '/Shared/test-file.txt')

await filesListPage.open('trashbin')
Expand Down
29 changes: 16 additions & 13 deletions tests/playwright/e2e/settings/personal-info.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,10 @@ test.describe('Settings: Change personal information', () => {
test.beforeAll(async () => {
// Prevent the Fediverse section from making outbound HTTP requests
await runOcc(['config:system:set', 'has_internet_connection', '--type', 'bool', '--value', 'false'])
// Let each user choose their own language and locale
await runOcc(['config:system:delete', 'force_language'])
await runOcc(['config:system:delete', 'force_locale'])
})

test.afterAll(async () => {
await runOcc(['config:system:delete', 'has_internet_connection'])
// Restore English defaults so other test suites are unaffected
await runOcc(['config:system:set', 'force_language', '--value', 'en'])
await runOcc(['config:system:set', 'force_locale', '--value', 'en_US'])
})

// ── Profile ───────────────────────────────────────────────────────────────
Expand All @@ -36,7 +30,7 @@ test.describe('Settings: Change personal information', () => {
await profileContactPage.setProfileEnabled(false)

// Profile is disabled: the public profile page shows a "not found" heading
await page.goto(`/u/${user.userId}`, { waitUntil: 'networkidle' })
await page.goto(`/u/${user.userId}`)
await expect(page.getByRole('heading', { name: /Profile not found/i })).toBeVisible()

// Re-enable the profile
Expand Down Expand Up @@ -343,13 +337,22 @@ test.describe('Settings: Change personal information', () => {
// ── Language & locale ─────────────────────────────────────────────────────

test.describe('Language & locale', () => {
test.beforeEach(async () => {
// The section is read-only while a language or locale is enforced, and a
// parallel worker may have restored the defaults of this suite already
// The section is read-only while a language or locale is enforced. Lifting
// the instance-wide enforcement must happen in a single worker, otherwise
// one worker's cleanup re-enforces it while another is still testing.
test.describe.configure({ mode: 'serial' })

test.beforeAll(async () => {
await runOcc(['config:system:delete', 'force_language'])
await runOcc(['config:system:delete', 'force_locale'])
})

test.afterAll(async () => {
// Restore the test server's English defaults for the other suites
await runOcc(['config:system:set', 'force_language', '--value', 'en'])
await runOcc(['config:system:set', 'force_locale', '--value', 'en_US'])
})

test('can change language', async ({ page, languageLocalePage }) => {
await languageLocalePage.open()

Expand All @@ -365,10 +368,10 @@ test.describe('Settings: Change personal information', () => {
// The example is rendered in the active locale, US English by default
await expect(languageLocalePage.localeExample()).toContainText(/Example: \d{2}\/\d{2}\/\d{4}/)

await languageLocalePage.selectLocale('German (Germany)', 'German')

// Locale change triggers a full page reload
await page.waitForLoadState('networkidle')
const reloaded = page.waitForEvent('load')
await languageLocalePage.selectLocale('German (Germany)', 'German')
await reloaded
await languageLocalePage.expectSelected(languageLocalePage.localeSelect(), 'German (Germany)')
// The example now uses the German date format
await expect(languageLocalePage.localeExample()).toContainText(/Example: \d{2}\.\d{2}\.\d{4}/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@ import { runOcc } from '@nextcloud/e2e-test-server'
import { expect } from '@playwright/test'
import { test } from '../../support/fixtures/systemtags-files-page.ts'
import { uploadContent } from '../../support/utils/dav.ts'
import { clearTags, createTag } from '../../support/utils/systemtags.ts'
import { createTag } from '../../support/utils/systemtags.ts'

test.beforeAll(async () => await runOcc(['config:app:set', 'systemtags', 'restrict_creation_to_admin', '--value', '1']))
test.afterAll(async () => await runOcc(['config:app:delete', 'systemtags', 'restrict_creation_to_admin']))
test.afterAll(async () => await clearTags())

test.beforeEach(async ({ filesListPage, page, user }) => {
await uploadContent(page.request, user, Buffer.alloc(0), 'text/plain', '/file1.txt')
await uploadContent(page.request, user, Buffer.alloc(0), 'text/plain', '/file2.txt')
await filesListPage.open()
})

test('Cannot create tag if restriction is in place', async ({ filesListPage }) => {
const tag = crypto.randomUUID()
test('Cannot create tag if restriction is in place', async ({ filesListPage, tagName }) => {
const tag = tagName()
await createTag(tag, 'public')

await filesListPage.expectInlineTagsForFile('file1.txt', [])
Expand Down
36 changes: 17 additions & 19 deletions tests/playwright/e2e/systemtags/admin-settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@
*/

import { expect } from '@playwright/test'
import { test } from '../../support/fixtures/admin-session.ts'
import { createTag, deleteTag, listTags } from '../../support/utils/systemtags.ts'
import { test as adminTest } from '../../support/fixtures/admin-session.ts'
import { createTag, tagNameFixture } from '../../support/utils/systemtags.ts'

const tagName = 'foo'
const updatedTagName = 'bar'
const test = adminTest.extend<{ tagName: () => string }>({
tagName: tagNameFixture,
})

test.describe('System tags admin settings', () => {
test.beforeEach(async () => {
const tags = await listTags()
for (const tag of tags) {
await deleteTag(tag.id)
}
})
// NcEllipsisedOption splits names of 10+ chars across two spans, which breaks the
// option's accessible name, so options are matched on their text content instead.

test('Can create a tag', async ({ page }) => {
test.describe('System tags admin settings', () => {
test('Can create a tag', async ({ page, tagName: generateTagName }) => {
const tagName = generateTagName()
await page.goto('settings/admin/server')

// Scroll the collaborative tags section into view — the admin settings page is long
Expand All @@ -35,18 +33,20 @@ test.describe('System tags admin settings', () => {

// The form resets after creation — verify the tag now appears in the selection dropdown
await page.getByRole('combobox', { name: 'Search for a tag to edit' }).click()
await expect(page.getByRole('option', { name: tagName })).toBeVisible()
await expect(page.getByRole('option').filter({ hasText: tagName })).toBeVisible()
})

test('Can update a tag', async ({ page }) => {
test('Can update a tag', async ({ page, tagName: generateTagName }) => {
const tagName = generateTagName()
const updatedTagName = generateTagName()
await createTag(tagName)

await page.goto('settings/admin/server')
await page.getByRole('heading', { name: 'Collaborative tags' }).scrollIntoViewIfNeeded()

// Select the tag to edit
await page.getByRole('combobox', { name: 'Search for a tag to edit' }).click()
await page.getByRole('option', { name: tagName }).click()
await page.getByRole('option').filter({ hasText: tagName }).click()

// Verify the form reflects the selected tag
await expect(page.getByLabel('Tag name')).toHaveValue(tagName)
Expand All @@ -65,13 +65,11 @@ test.describe('System tags admin settings', () => {
expect((await updateResponse).status()).toBe(207)

await page.getByRole('combobox', { name: 'Search for a tag to edit' }).click()
// NcEllipsisedOption splits names ≥ 10 chars across two spans, breaking the accessible name.
// "bar (invisible)" (15 chars) splits at position 8 → accessible name "bar (inv isible)".
// Use filter({ hasText }) to match on text content instead of the exact accessible name.
await expect(page.getByRole('option').filter({ hasText: updatedTagName })).toBeVisible()
})

test('Can delete a tag', async ({ page }) => {
test('Can delete a tag', async ({ page, tagName: generateTagName }) => {
const tagName = generateTagName()
await createTag(tagName)

await page.goto('settings/admin/server')
Expand Down
40 changes: 19 additions & 21 deletions tests/playwright/e2e/systemtags/files-bulk-action.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { test as baseTest } from '../../support/fixtures/systemtags-files-page.ts'
import { uploadContent } from '../../support/utils/dav.ts'
import { assignTagsToFile, clearTags, createTag } from '../../support/utils/systemtags.ts'
import { assignTagsToFile, createTag } from '../../support/utils/systemtags.ts'

// Extends the base fixture with per-test file IDs so tests in parallel each get
// their own isolated file IDs rather than sharing module-level mutable state.
Expand All @@ -19,11 +19,9 @@ const test = baseTest.extend<{ fileIds: [string, string] }>({
})

test.describe('Systemtags: Files bulk action', () => {
test.afterAll(async () => await clearTags())

// eslint-disable-next-line @typescript-eslint/no-unused-vars -- Needed to execute the upload by PlayWright
test('Can assign tag to selection', async ({ filesListPage, fileIds }) => {
const tag = crypto.randomUUID()
test('Can assign tag to selection', async ({ filesListPage, fileIds, tagName }) => {
const tag = tagName()

await filesListPage.expectInlineTagsForFile('file1.txt', [])
await filesListPage.expectInlineTagsForFile('file2.txt', [])
Expand All @@ -39,9 +37,9 @@ test.describe('Systemtags: Files bulk action', () => {
await filesListPage.expectInlineTagsForFile('file2.txt', [tag])
})

test('Can assign multiple tags to selection', async ({ filesListPage }) => {
const tag1 = crypto.randomUUID()
const tag2 = crypto.randomUUID()
test('Can assign multiple tags to selection', async ({ filesListPage, tagName }) => {
const tag1 = tagName()
const tag2 = tagName()

await filesListPage.expectInlineTagsForFile('file1.txt', [])
await filesListPage.expectInlineTagsForFile('file2.txt', [])
Expand All @@ -58,9 +56,9 @@ test.describe('Systemtags: Files bulk action', () => {
await filesListPage.expectInlineTagsForFile('file2.txt', [tag1, tag2])
})

test('Can remove tag from selection', async ({ filesListPage, page, fileIds }) => {
const tag1 = crypto.randomUUID()
const tag2 = crypto.randomUUID()
test('Can remove tag from selection', async ({ filesListPage, page, fileIds, tagName }) => {
const tag1 = tagName()
const tag2 = tagName()
await assignTagsToFile(fileIds[0], [tag1, tag2])
await assignTagsToFile(fileIds[1], [tag1, tag2])
await page.reload()
Expand All @@ -79,10 +77,10 @@ test.describe('Systemtags: Files bulk action', () => {
await filesListPage.expectInlineTagsForFile('file2.txt', [tag1])
})

test('Can remove multiple tags from selection', async ({ filesListPage, page, fileIds }) => {
const tag1 = crypto.randomUUID()
const tag2 = crypto.randomUUID()
const tag3 = crypto.randomUUID()
test('Can remove multiple tags from selection', async ({ filesListPage, page, fileIds, tagName }) => {
const tag1 = tagName()
const tag2 = tagName()
const tag3 = tagName()
await assignTagsToFile(fileIds[0], [tag1, tag2, tag3])
await assignTagsToFile(fileIds[1], [tag1, tag2, tag3])
await page.reload()
Expand All @@ -102,10 +100,10 @@ test.describe('Systemtags: Files bulk action', () => {
await filesListPage.expectInlineTagsForFile('file2.txt', [tag1])
})

test('Can assign and remove multiple tags', async ({ filesListPage, page, fileIds }) => {
const tag1 = crypto.randomUUID()
const tag2 = crypto.randomUUID()
const tag3 = crypto.randomUUID()
test('Can assign and remove multiple tags', async ({ filesListPage, page, fileIds, tagName }) => {
const tag1 = tagName()
const tag2 = tagName()
const tag3 = tagName()
await assignTagsToFile(fileIds[0], [tag1, tag2])
await assignTagsToFile(fileIds[1], [tag1, tag2])
await page.reload()
Expand All @@ -125,8 +123,8 @@ test.describe('Systemtags: Files bulk action', () => {
await filesListPage.expectInlineTagsForFile('file2.txt', [tag1, tag3])
})

test('Can search for tags with insensitive case', async ({ filesListPage }) => {
const tag = crypto.randomUUID().toLowerCase()
test('Can search for tags with insensitive case', async ({ filesListPage, tagName }) => {
const tag = tagName()
await createTag(tag, 'public')

await filesListPage.expectInlineTagsForFile('file1.txt', [])
Expand Down
21 changes: 9 additions & 12 deletions tests/playwright/e2e/systemtags/files-inline-action.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@

import { expect, test } from '../../support/fixtures/systemtags-files-page.ts'
import { uploadContent } from '../../support/utils/dav.ts'
import { clearTags } from '../../support/utils/systemtags.ts'

test.describe('Systemtags: Files integration', () => {
test.afterAll(async () => await clearTags())

test.beforeEach(async ({ page, user, filesListPage }) => {
await uploadContent(page.request, user, Buffer.alloc(0), 'text/plain', '/file.txt')
await filesListPage.open()
})

test('See first assigned tag in the file list', async ({ page, filesListPage }) => {
const tag = crypto.randomUUID()
test('See first assigned tag in the file list', async ({ page, filesListPage, tagName }) => {
const tag = tagName()

await filesListPage.openTagPickerForFile('file.txt')
await filesListPage.createNewTagInPicker(tag)
Expand All @@ -29,9 +26,9 @@ test.describe('Systemtags: Files integration', () => {
await expect(tagList.getByRole('listitem')).toContainText(tag)
})

test('See two assigned tags are also shown in the file list', async ({ page, filesListPage }) => {
const tag1 = crypto.randomUUID()
const tag2 = crypto.randomUUID()
test('See two assigned tags are also shown in the file list', async ({ page, filesListPage, tagName }) => {
const tag1 = tagName()
const tag2 = tagName()

await filesListPage.openTagPickerForFile('file.txt')
await filesListPage.createNewTagInPicker(tag1)
Expand All @@ -46,10 +43,10 @@ test.describe('Systemtags: Files integration', () => {
await expect(tagList).toContainText(tag2)
})

test('See three assigned tags result in overflow entry', async ({ page, filesListPage }) => {
const tag1 = crypto.randomUUID()
const tag2 = crypto.randomUUID()
const tag3 = crypto.randomUUID()
test('See three assigned tags result in overflow entry', async ({ page, filesListPage, tagName }) => {
const tag1 = tagName()
const tag2 = tagName()
const tag3 = tagName()

await filesListPage.openTagPickerForFile('file.txt')
await filesListPage.createNewTagInPicker(tag1)
Expand Down
7 changes: 2 additions & 5 deletions tests/playwright/e2e/systemtags/files-sidebar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@

import { expect, test } from '../../support/fixtures/systemtags-files-page.ts'
import { uploadContent } from '../../support/utils/dav.ts'
import { clearTags } from '../../support/utils/systemtags.ts'

test.describe('Systemtags: Files sidebar integration', () => {
test.afterAll(async () => await clearTags())

test.beforeEach(async ({ page, user, filesListPage }) => {
await uploadContent(page.request, user, Buffer.alloc(0), 'text/plain', '/file.txt')
await filesListPage.open()
})

test('Can assign tags using the sidebar', async ({ filesListPage, filesSidebar }) => {
const tag = crypto.randomUUID()
test('Can assign tags using the sidebar', async ({ filesListPage, filesSidebar, tagName }) => {
const tag = tagName()

await expect(filesListPage.getRowForFile('file.txt')).toBeVisible()

Expand Down
7 changes: 2 additions & 5 deletions tests/playwright/e2e/systemtags/files-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@

import { expect, test } from '../../support/fixtures/systemtags-files-page.ts'
import { mkdir, uploadContent } from '../../support/utils/dav.ts'
import { clearTags } from '../../support/utils/systemtags.ts'

test.describe('Systemtags: Files view', () => {
test.afterAll(async () => await clearTags())

test.beforeEach(async ({ page, user, filesListPage }) => {
await mkdir(page.request, user, '/folder')
await uploadContent(page.request, user, Buffer.alloc(0), 'text/plain', '/file.txt')
await filesListPage.open()
})

test('See first assigned tag in the file list', async ({ page, filesListPage }) => {
const tag = crypto.randomUUID()
test('See first assigned tag in the file list', async ({ page, filesListPage, tagName }) => {
const tag = tagName()

await expect(filesListPage.getRowForFile('folder')).toBeVisible()
await expect(filesListPage.getRowForFile('file.txt')).toBeVisible()
Expand Down
Loading
Loading