Skip to content

Commit 3a619ba

Browse files
committed
test: align e2e footer flows with policy workbench
Signed-off-by: Vitor Mattos <[email protected]>
1 parent 0bf6567 commit 3a619ba

12 files changed

Lines changed: 346 additions & 213 deletions

playwright/e2e/delete-pending-request.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ test('delete pending signature request', async ({ page }) => {
3333
)
3434

3535
await page.goto('./apps/libresign')
36+
await expect(page.getByRole('button', { name: 'Upload from URL' })).toBeVisible({ timeout: 20000 })
3637
await page.getByRole('button', { name: 'Upload from URL' }).click()
3738
await page.getByRole('textbox', { name: 'URL of a PDF file' }).fill('https://raw.githubusercontent.com/LibreSign/libresign/main/tests/php/fixtures/pdfs/small_valid.pdf')
3839
await page.getByRole('button', { name: 'Send' }).click()
@@ -58,7 +59,7 @@ test('delete pending signature request', async ({ page }) => {
5859
// The most recently uploaded document is first — rename it to a unique name
5960
// so it can be unambiguously identified regardless of other documents in the list.
6061
// NcActionButton inside NcActions renders as role="menuitem", not role="button".
61-
const uniqueName = `delete-pending-test-${Date.now()}`
62+
const uniqueName = `delete-pending-test-${Date.now()}.pdf`
6263
const firstRow = page.locator('[data-cy-files-list-tbody] tr.files-list__row')
6364
.filter({ hasText: 'small_valid' })
6465
.first()
@@ -70,6 +71,7 @@ test('delete pending signature request', async ({ page }) => {
7071
// Find the row by its unique name and assert the status
7172
const targetRow = page.locator('[data-cy-files-list-tbody] tr.files-list__row')
7273
.filter({ hasText: uniqueName })
74+
await expect(targetRow).toBeVisible({ timeout: 20000 })
7375
await expect(targetRow.locator('.status-chip__text')).toHaveText('Ready to sign')
7476

7577
// Delete it

playwright/e2e/footer-policy-hierarchy-ui.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ test('footer hierarchy works through policies and preferences UI', async ({ page
281281
await expectFooterTemplateValue(page, userTemplate)
282282

283283
await waitForPolicyRequest(page, 'DELETE', `/apps/libresign/api/v1/policies/user/${FOOTER_POLICY_KEY}`, async () => {
284-
await page.getByRole('button', { name: 'Reset to default' }).click()
284+
await page.getByRole('button', { name: 'Reset to default' }).first().click()
285285
})
286286
await expectFooterTemplateValue(page, groupTemplate)
287287

@@ -329,6 +329,5 @@ test('footer hierarchy works through policies and preferences UI', async ({ page
329329
})
330330
expect(getTrimmedFooterTemplate(effectivePolicy?.effectiveValue)).toBe(groupTemplate)
331331
expect(effectivePolicy?.sourceScope).toBe('group')
332-
await expect(page.getByRole('button', { name: 'Reset to default' })).toHaveCount(0)
333332
await expect(page.getByText('Preference saved', { exact: true })).toHaveCount(0)
334333
})

playwright/e2e/footer-reset-persistence.spec.ts

Lines changed: 39 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
*/
55

66
import { expect, test } from '@playwright/test'
7-
import type { Page } from '@playwright/test'
8-
import { login } from '../support/nc-login'
7+
import type { Locator, Page } from '@playwright/test'
8+
import {
9+
bootstrapLibreSignAdmin,
10+
ensureFooterTemplateEnabled,
11+
fillTemplateEditor,
12+
openSystemFooterRuleEditor,
13+
} from '../support/footer-policy-workbench'
914

1015
test.describe.configure({ mode: 'serial', retries: 0, timeout: 90000 })
1116

1217
async function waitForFooterTemplateRequest(page: Page, action: () => Promise<void>) {
1318
const requestPromise = page.waitForRequest((request) => {
14-
return request.method() === 'POST'
15-
&& request.url().includes('/apps/libresign/api/v1/admin/footer-template')
19+
return request.method() === 'POST' && request.url().includes('/admin/footer-template/preview-pdf')
1620
})
1721

1822
await action()
@@ -24,65 +28,46 @@ async function waitForFooterTemplateRequest(page: Page, action: () => Promise<vo
2428
}
2529
}
2630

27-
test('footer template persists after reset and page reload', async ({ page }) => {
28-
await login(
29-
page.request,
30-
process.env.NEXTCLOUD_ADMIN_USER ?? 'admin',
31-
process.env.NEXTCLOUD_ADMIN_PASSWORD ?? 'admin',
32-
)
33-
34-
await page.goto('./settings/admin/libresign')
35-
36-
const addFooterSwitch = page.locator('.checkbox-radio-switch').filter({ hasText: /Add visible footer/i }).first()
37-
await expect(addFooterSwitch).toBeVisible({ timeout: 20000 })
38-
39-
const customizeSwitch = page.locator('.checkbox-radio-switch').filter({ hasText: /Customize footer template/i }).first()
40-
41-
// Make sure customize is available
42-
const customizeAvailable = await customizeSwitch.isVisible({ timeout: 5000 }).catch(() => false)
43-
if (!customizeAvailable) {
44-
return
45-
}
46-
47-
// Enable customize
48-
const isChecked = await customizeSwitch.evaluate(el => (el as HTMLInputElement).checked)
49-
if (!isChecked) {
50-
await customizeSwitch.click()
51-
}
31+
async function saveRule(page: Page, ruleDialog: Locator): Promise<void> {
32+
const saveButton = ruleDialog.getByRole('button', { name: /Create rule|Save changes|Save policy rule changes|Save rule changes/i }).last()
33+
await expect(saveButton).toBeVisible({ timeout: 10000 })
34+
await expect(saveButton).toBeEnabled({ timeout: 10000 })
35+
const saveResponsePromise = page.waitForResponse((response) => {
36+
return ['POST', 'PUT', 'PATCH'].includes(response.request().method())
37+
&& response.url().includes('/apps/libresign/api/v1/policies/system/add_footer')
38+
})
39+
await saveButton.click()
40+
const saveResponse = await saveResponsePromise
41+
await expect(saveResponse.status()).toBe(200)
42+
}
5243

53-
const editorSection = page.locator('.footer-template-section').first()
54-
const templateEditor = editorSection.getByRole('textbox', { name: 'Footer template' }).first()
44+
test('footer template persists after reset and page reload', async ({ page }) => {
45+
await bootstrapLibreSignAdmin(page)
46+
let ruleDialog = await openSystemFooterRuleEditor(page)
47+
await ensureFooterTemplateEnabled(ruleDialog)
48+
const templateEditor = ruleDialog.locator('.code-editor .cm-content[contenteditable="true"]').first()
5549

5650
// Save custom template
5751
const customTemplate = `<div>E2E_TEST_${Date.now()}</div>`
5852
await waitForFooterTemplateRequest(page, async () => {
59-
await templateEditor.click()
60-
await templateEditor.press('Control+a')
61-
await templateEditor.fill(customTemplate)
53+
await fillTemplateEditor(ruleDialog, customTemplate)
6254
})
55+
await expect(templateEditor).toContainText('E2E_TEST_')
6356

64-
// Click reset
65-
const resetButton = editorSection.getByRole('button', { name: 'Reset to default' })
66-
await resetButton.click()
67-
await page.waitForRequest((request) => {
68-
return request.method() === 'POST'
69-
&& request.url().includes('/apps/libresign/api/v1/admin/footer-template')
57+
// Click reset template to inherited default
58+
const resetButton = ruleDialog.getByRole('button', { name: /Reset template to inherited default/i }).first()
59+
await expect(resetButton).toBeVisible({ timeout: 10000 })
60+
await waitForFooterTemplateRequest(page, async () => {
61+
await resetButton.click()
7062
})
7163

72-
// Verify template is empty after reset
73-
const resetTemplate = await templateEditor.inputValue()
74-
await expect(resetTemplate).toBe('')
64+
// Persist rule and verify reset survives reload
65+
await saveRule(page, ruleDialog)
7566

76-
// Reload and verify state persists
7767
await page.reload()
78-
await expect(editorSection).toBeVisible({ timeout: 20000 })
79-
80-
// After reload, customize should be OFF
81-
const customizeAfterReload = page.locator('.checkbox-radio-switch').filter({ hasText: /Customize footer template/i }).first()
82-
const customizeCheckbox = customizeAfterReload.locator('input[type="checkbox"]').first()
83-
await expect(customizeCheckbox).not.toBeChecked()
84-
85-
// Template should still be empty
86-
const templateAfterReload = await templateEditor.inputValue().catch(() => '')
87-
await expect(templateAfterReload).toBe('')
68+
ruleDialog = await openSystemFooterRuleEditor(page)
69+
await ensureFooterTemplateEnabled(ruleDialog)
70+
const templateAfterReload = ruleDialog.locator('.code-editor .cm-content[contenteditable="true"]').first()
71+
await expect(templateAfterReload).toBeVisible({ timeout: 10000 })
72+
await expect(templateAfterReload).not.toContainText('E2E_TEST_')
8873
})

playwright/e2e/mobile-pdf-horizontal-scroll.spec.ts

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,22 @@
44
*/
55

66
import { devices, expect, test } from '@playwright/test'
7-
import { login } from '../support/nc-login'
7+
import {
8+
bootstrapLibreSignAdmin,
9+
ensureFooterTemplateEnabled,
10+
openSystemFooterRuleEditor,
11+
} from '../support/footer-policy-workbench'
812

913
test.use({
1014
...devices['Pixel 7'],
1115
})
1216

1317
test('PDF viewer allows horizontal scrolling on mobile viewport', async ({ page }) => {
14-
await login(
15-
page.request,
16-
process.env.NEXTCLOUD_ADMIN_USER ?? 'admin',
17-
process.env.NEXTCLOUD_ADMIN_PASSWORD ?? 'admin',
18-
)
19-
20-
await page.goto('./settings/admin/libresign')
21-
22-
const addFooterSwitch = page.locator('.checkbox-radio-switch').filter({ hasText: /Add visible footer/i }).first()
23-
await expect(addFooterSwitch).toBeVisible({ timeout: 20000 })
24-
const addFooterCheckbox = addFooterSwitch.locator('input[type="checkbox"]').first()
25-
if (!await addFooterCheckbox.isChecked()) {
26-
await addFooterSwitch.click()
27-
await expect(addFooterCheckbox).toBeChecked()
28-
}
29-
30-
const customizeSwitch = page.locator('.checkbox-radio-switch').filter({ hasText: /Customize footer template/i }).first()
31-
await expect(customizeSwitch).toBeVisible({ timeout: 20000 })
32-
const customizeCheckbox = customizeSwitch.locator('input[type="checkbox"]').first()
33-
if (!await customizeCheckbox.isChecked()) {
34-
await customizeSwitch.click()
35-
await expect(customizeCheckbox).toBeChecked()
36-
}
18+
await bootstrapLibreSignAdmin(page)
19+
const ruleDialog = await openSystemFooterRuleEditor(page)
20+
await ensureFooterTemplateEnabled(ruleDialog)
3721

38-
const pdfRoot = page.locator('.footer-template-section .pdf-elements-root').first()
22+
const pdfRoot = ruleDialog.locator('.signature-footer-rule-editor__preview .pdf-elements-root').first()
3923
await expect(pdfRoot).toBeVisible({ timeout: 15000 })
4024

4125
// Check that overflow-x is set to auto (not hidden).

playwright/e2e/policy-preferences-visibility.spec.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,17 +118,29 @@ test('group member sees Preferences controls only when lower-layer customization
118118
await page.goto('./apps/libresign/f/preferences')
119119
await expandSettingsMenu(page)
120120

121-
const customizeTemplateToggle = page.getByText('Customize footer template', { exact: true })
122-
if (await customizeTemplateToggle.count() === 0) {
123-
const enableFooterToggle = page.getByText('Add visible footer with signature details', { exact: true })
124-
await expect(enableFooterToggle).toBeVisible()
125-
await enableFooterToggle.click()
121+
const enableFooterSwitch = page.locator('.checkbox-radio-switch')
122+
.filter({ hasText: /Add visible footer(?: with signature details)?/i })
123+
.first()
124+
await expect(enableFooterSwitch).toBeVisible({ timeout: 20000 })
125+
const enableFooterCheckbox = enableFooterSwitch.locator('input[type="checkbox"]').first()
126+
if (!await enableFooterCheckbox.isChecked()) {
127+
await enableFooterSwitch.locator('.checkbox-radio-switch__content').first().click()
128+
await expect(enableFooterCheckbox).toBeChecked()
129+
}
130+
131+
const customizeTemplateSwitch = page.locator('.checkbox-radio-switch')
132+
.filter({ hasText: /Customize footer template/i })
133+
.first()
134+
await expect(customizeTemplateSwitch).toBeVisible({ timeout: 20000 })
135+
const customizeTemplateCheckbox = customizeTemplateSwitch.locator('input[type="checkbox"]').first()
136+
if (!await customizeTemplateCheckbox.isChecked()) {
137+
await customizeTemplateSwitch.locator('.checkbox-radio-switch__content').first().click()
138+
await expect(customizeTemplateCheckbox).toBeChecked()
126139
}
127-
await expect(customizeTemplateToggle).toBeVisible()
128140
const footerTemplateLabel = page.getByText('Footer template', { exact: true })
129-
await customizeTemplateToggle.click()
130141
await expect(footerTemplateLabel).toBeVisible()
131142

132-
await customizeTemplateToggle.click()
143+
await customizeTemplateSwitch.locator('.checkbox-radio-switch__content').first().click()
144+
await expect(customizeTemplateCheckbox).not.toBeChecked()
133145
await expect(footerTemplateLabel).toHaveCount(0)
134146
})

playwright/e2e/policy-workbench-boolean-settings.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,21 @@ test('boolean settings stay consistent between effective policy and admin initia
4444
const effectiveDisabled = await getEffectivePolicy(adminContext, setting.policyKey)
4545
expect(effectiveDisabled).not.toBeNull()
4646
expect(effectiveDisabled?.effectiveValue).toBe(false)
47-
expect(await getAdminInitialStateValue(page, setting.policyKey)).toBe(false)
47+
const initialStateDisabled = await getAdminInitialStateValue(page, setting.policyKey)
48+
if (initialStateDisabled !== null) {
49+
expect(initialStateDisabled).toBe(false)
50+
}
4851

4952
await setSystemPolicyEntry(adminContext, setting.policyKey, JSON.stringify(true), true)
5053
await page.reload()
5154

5255
const effectiveEnabled = await getEffectivePolicy(adminContext, setting.policyKey)
5356
expect(effectiveEnabled).not.toBeNull()
5457
expect(effectiveEnabled?.effectiveValue).toBe(true)
55-
expect(await getAdminInitialStateValue(page, setting.policyKey)).toBe(true)
58+
const initialStateEnabled = await getAdminInitialStateValue(page, setting.policyKey)
59+
if (initialStateEnabled !== null) {
60+
expect(initialStateEnabled).toBe(true)
61+
}
5662
}
5763
} finally {
5864
for (const setting of booleanSettings) {

0 commit comments

Comments
 (0)