diff --git a/playwright/e2e/sign-envelope-unauthenticated-visible-signature.spec.ts b/playwright/e2e/sign-envelope-unauthenticated-visible-signature.spec.ts index d2cee78a4b..3e93e95a17 100644 --- a/playwright/e2e/sign-envelope-unauthenticated-visible-signature.spec.ts +++ b/playwright/e2e/sign-envelope-unauthenticated-visible-signature.spec.ts @@ -87,6 +87,9 @@ async function enableEnvelopeScenario(request: APIRequestContext) { await setCertificateEngine(request, 'openssl') + // This flow opens signed PDFs as an anonymous signer; do not inherit a + // private validation policy left by another Playwright scenario. + await setSystemPolicy(request, 'make_validation_url_private', '0') await setSystemPolicy(request, 'envelope_enabled', '1') await setSystemPolicy(request, 'identification_documents', JSON.stringify({ enabled: false, approvers: ['admin'] })) await setSystemPolicy( @@ -160,7 +163,7 @@ async function waitForSignerInvitationLink(signerEmail: string) { const email = await waitForEmailTo( createMailpitClient(), signerEmail, - 'LibreSign: A document is ready for your signature', + undefined, // Notification language can differ from the test browser locale. { interval: 250 }, ) const signLink = extractSignLink(email.Text) @@ -285,4 +288,36 @@ test('unauthenticated signer can define a visible signature for an envelope with await test.step('Then the success confirmation screen is shown with the envelope name', async () => { await expectEnvelopeSigned(page, scenario.envelopeName) }) + + await test.step('The signed PDF opens by UUID without using an Office handler', async () => { + const context = page.context() + const officeRequests: string[] = [] + context.on('request', request => { + if (request.url().includes('/apps/eurooffice/')) { + officeRequests.push(request.url()) + } + }) + + // The legacy Viewer cannot load anonymous PDFs through authenticated + // Files APIs. Check the direct URL fallback in this public signing flow. + const popupPromise = page.waitForEvent('popup') + const pdfResponse = context.waitForEvent('response', response => + response.request().method() === 'GET' + && response.url().includes('/apps/libresign/p/pdf/'), + ) + await page.getByRole('button', { name: 'Show details' }).first().click() + await page.getByRole('button', { name: 'View document' }).first().click() + + const popup = await popupPromise + expect(await popup.opener()).toBe(page) + const response = await pdfResponse + expect(response.status()).toBe(200) + expect(response.headers()['content-type']).toContain('application/pdf') + expect(new URL(response.url()).pathname).toMatch(/\/apps\/libresign\/p\/pdf\/[^/]+$/) + + const signedPdf = await page.request.get(response.url()) + expect(signedPdf.status()).toBe(200) + expect((await signedPdf.body()).toString('latin1')).toContain('/ByteRange') + expect(officeRequests).toHaveLength(0) + }) }) diff --git a/src/components/validation/DocumentValidationDetails.vue b/src/components/validation/DocumentValidationDetails.vue index ed337688b0..a92bfb08df 100644 --- a/src/components/validation/DocumentValidationDetails.vue +++ b/src/components/validation/DocumentValidationDetails.vue @@ -99,14 +99,13 @@ const size = computed(() => { const documentStatus = computed(() => getStatusLabel(document.value.status)) async function viewDocument() { - if (!document.value.uuid || !document.value.name || typeof document.value.nodeId !== 'number') { + if (!document.value.uuid || !document.value.name) { return } const fileUrl = generateUrl('/apps/libresign/p/pdf/{uuid}', { uuid: document.value.uuid }) await openDocument({ fileUrl, filename: document.value.name, - nodeId: document.value.nodeId, }) } diff --git a/src/components/validation/EnvelopeValidation.vue b/src/components/validation/EnvelopeValidation.vue index e530f1f9cf..b0cc883e85 100644 --- a/src/components/validation/EnvelopeValidation.vue +++ b/src/components/validation/EnvelopeValidation.vue @@ -65,7 +65,7 @@ -