From 02ff8df09ba8033ef7ea2006ba73f5e0f69c9ccf Mon Sep 17 00:00:00 2001 From: ephraimduncan Date: Thu, 1 Jan 2026 23:04:26 +0000 Subject: [PATCH] chore: remove tests - temp --- .../e2e/envelopes/file-conversion.spec.ts | 1166 ----------------- 1 file changed, 1166 deletions(-) delete mode 100644 packages/app-tests/e2e/envelopes/file-conversion.spec.ts diff --git a/packages/app-tests/e2e/envelopes/file-conversion.spec.ts b/packages/app-tests/e2e/envelopes/file-conversion.spec.ts deleted file mode 100644 index 323e8290c..000000000 --- a/packages/app-tests/e2e/envelopes/file-conversion.spec.ts +++ /dev/null @@ -1,1166 +0,0 @@ -import type { Page } from '@playwright/test'; -import { expect, test } from '@playwright/test'; -import { DocumentStatus, EnvelopeType, FieldType } from '@prisma/client'; -import { nanoid } from 'nanoid'; -import fs from 'node:fs'; -import path from 'node:path'; - -import { prisma } from '@documenso/prisma'; -import { seedUser } from '@documenso/prisma/seed/users'; - -import { NEXT_PUBLIC_WEBAPP_URL } from '../../../lib/constants/app'; -import { createApiToken } from '../../../lib/server-only/public-api/create-api-token'; -import { RecipientRole } from '../../../prisma/generated/types'; -import type { - TCreateEnvelopePayload, - TCreateEnvelopeResponse, -} from '../../../trpc/server/envelope-router/create-envelope.types'; -import type { TDistributeEnvelopeRequest } from '../../../trpc/server/envelope-router/distribute-envelope.types'; -import { apiSignin } from '../fixtures/authentication'; - -const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL(); -const baseUrl = `${WEBAPP_BASE_URL}/api/v2`; - -test.describe.configure({ mode: 'parallel', timeout: 60000 }); - -/** - * Helper to verify originalData is populated for converted files. - * PNG/JPEG/DOCX should have originalData, PDF should not. - */ -async function verifyOriginalData(envelopeId: string, expectOriginalData: boolean) { - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: envelopeId }, - include: { - envelopeItems: { - include: { documentData: true }, - }, - }, - }); - - if (expectOriginalData) { - expect(envelope.envelopeItems[0].documentData?.originalData).not.toBeNull(); - expect(envelope.envelopeItems[0].documentData?.originalMimeType).not.toBeNull(); - } else { - expect(envelope.envelopeItems[0].documentData?.originalData).toBeNull(); - } -} - -/** - * Helper to mark signature fields as inserted via Prisma. - * This bypasses Konva canvas interaction which is hard to test. - * Based on the pattern used in envelope-alignment.spec.ts. - */ -async function markSignatureFieldsAsInserted(envelopeId: string, recipientId: number) { - const signatureFields = await prisma.field.findMany({ - where: { - envelopeId, - recipientId, - type: FieldType.SIGNATURE, - inserted: false, - }, - }); - - for (const field of signatureFields) { - await prisma.field.update({ - where: { id: field.id }, - data: { - inserted: true, - customText: '', - signature: { - create: { - recipientId, - typedSignature: 'Test Signature', - signatureImageAsBase64: null, - }, - }, - }, - }); - } -} - -/** - * Helper to complete the signing flow for a recipient. - * Expects fields to already be marked as inserted via Prisma. - */ -async function completeSigningFlow(page: Page) { - await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible(); - - // Wait for Complete button to be visible (fields should already be inserted) - await expect(page.getByRole('button', { name: 'Complete' })).toBeVisible({ timeout: 10000 }); - - // Click Complete button - await page.getByRole('button', { name: 'Complete' }).click(); - - // Wait for Sign button to be visible in the confirmation dialog - const signButton = page.getByRole('button', { name: 'Sign' }); - await expect(signButton).toBeVisible({ timeout: 10000 }); - - // Use force click to handle transient DOM changes - await signButton.click({ force: true }); - - // Wait for "Document Signed" heading to appear (confirms completion) - await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible({ - timeout: 30000, - }); -} - -test.describe('File Conversion', () => { - test.describe('API Tests', () => { - test('[FILE_CONVERSION_API]: should upload, convert, and sign a PNG image via API', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - const pngFile = fs.readFileSync(path.join(__dirname, '../../../../assets/test-image.png')); - - const formData = new FormData(); - - const createEnvelopePayload: TCreateEnvelopePayload = { - type: EnvelopeType.DOCUMENT, - title: 'PNG Conversion Test', - recipients: [ - { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - fields: [ - { - type: FieldType.SIGNATURE, - fieldMeta: { type: 'signature' }, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - }, - ], - }, - ], - }; - - formData.append('payload', JSON.stringify(createEnvelopePayload)); - formData.append('files', new File([pngFile], 'test-image.png', { type: 'image/png' })); - - const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, { - headers: { Authorization: `Bearer ${token}` }, - multipart: formData, - }); - - if (!createEnvelopeRequest.ok()) { - const errorBody = await createEnvelopeRequest.json(); - const errorCode = - errorBody.code === 'INTERNAL_SERVER_ERROR' - ? errorBody.data?.code || errorBody.data?.appError?.code - : errorBody.code; - if (errorCode === 'CONVERSION_SERVICE_UNAVAILABLE' || errorCode === 'CONVERSION_FAILED') { - test.skip(true, 'Gotenberg conversion service is not available'); - return; - } - throw new Error(`Failed to create envelope: ${JSON.stringify(errorBody)}`); - } - - expect(createEnvelopeRequest.status()).toBe(200); - - const { id: createdEnvelopeId }: TCreateEnvelopeResponse = await createEnvelopeRequest.json(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: createdEnvelopeId }, - include: { - recipients: true, - envelopeItems: true, - }, - }); - - expect(envelope.envelopeItems.length).toBe(1); - expect(envelope.recipients.length).toBe(1); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - expect(distributeEnvelopeRequest.ok()).toBeTruthy(); - - const recipientToken = envelope.recipients[0].token; - const signUrl = `/sign/${recipientToken}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, envelope.recipients[0].id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - await verifyOriginalData(envelope.id, true); - }); - - test('[FILE_CONVERSION_API]: should upload, convert, and sign a JPEG image via API', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - const jpegFile = fs.readFileSync(path.join(__dirname, '../../../../assets/test-image.jpg')); - - const formData = new FormData(); - - const createEnvelopePayload: TCreateEnvelopePayload = { - type: EnvelopeType.DOCUMENT, - title: 'JPEG Conversion Test', - recipients: [ - { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - fields: [ - { - type: FieldType.SIGNATURE, - fieldMeta: { type: 'signature' }, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - }, - ], - }, - ], - }; - - formData.append('payload', JSON.stringify(createEnvelopePayload)); - formData.append('files', new File([jpegFile], 'test-image.jpg', { type: 'image/jpeg' })); - - const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, { - headers: { Authorization: `Bearer ${token}` }, - multipart: formData, - }); - - if (!createEnvelopeRequest.ok()) { - const errorBody = await createEnvelopeRequest.json(); - const errorCode = - errorBody.code === 'INTERNAL_SERVER_ERROR' - ? errorBody.data?.code || errorBody.data?.appError?.code - : errorBody.code; - if (errorCode === 'CONVERSION_SERVICE_UNAVAILABLE' || errorCode === 'CONVERSION_FAILED') { - test.skip(true, 'Gotenberg conversion service is not available'); - return; - } - throw new Error(`Failed to create envelope: ${JSON.stringify(errorBody)}`); - } - - expect(createEnvelopeRequest.status()).toBe(200); - - const { id: createdEnvelopeId }: TCreateEnvelopeResponse = await createEnvelopeRequest.json(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: createdEnvelopeId }, - include: { - recipients: true, - envelopeItems: true, - }, - }); - - expect(envelope.envelopeItems.length).toBe(1); - expect(envelope.recipients.length).toBe(1); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - expect(distributeEnvelopeRequest.ok()).toBeTruthy(); - - const recipientToken = envelope.recipients[0].token; - const signUrl = `/sign/${recipientToken}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, envelope.recipients[0].id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - await verifyOriginalData(envelope.id, true); - }); - - test('[FILE_CONVERSION_API]: should upload, convert, and sign a DOCX document via API', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - const docxFile = fs.readFileSync( - path.join(__dirname, '../../../../assets/test-document.docx'), - ); - - const formData = new FormData(); - - const createEnvelopePayload: TCreateEnvelopePayload = { - type: EnvelopeType.DOCUMENT, - title: 'DOCX Conversion Test', - recipients: [ - { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - fields: [ - { - type: FieldType.SIGNATURE, - fieldMeta: { type: 'signature' }, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - }, - ], - }, - ], - }; - - formData.append('payload', JSON.stringify(createEnvelopePayload)); - formData.append( - 'files', - new File([docxFile], 'test-document.docx', { - type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - }), - ); - - const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, { - headers: { Authorization: `Bearer ${token}` }, - multipart: formData, - }); - - if (!createEnvelopeRequest.ok()) { - const errorBody = await createEnvelopeRequest.json(); - const errorCode = - errorBody.code === 'INTERNAL_SERVER_ERROR' - ? errorBody.data?.code || errorBody.data?.appError?.code - : errorBody.code; - if (errorCode === 'CONVERSION_SERVICE_UNAVAILABLE' || errorCode === 'CONVERSION_FAILED') { - test.skip(true, 'Gotenberg conversion service is not available'); - return; - } - throw new Error(`Failed to create envelope: ${JSON.stringify(errorBody)}`); - } - - expect(createEnvelopeRequest.status()).toBe(200); - - const { id: createdEnvelopeId }: TCreateEnvelopeResponse = await createEnvelopeRequest.json(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: createdEnvelopeId }, - include: { - recipients: true, - envelopeItems: true, - }, - }); - - expect(envelope.envelopeItems.length).toBe(1); - expect(envelope.recipients.length).toBe(1); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - expect(distributeEnvelopeRequest.ok()).toBeTruthy(); - - const recipientToken = envelope.recipients[0].token; - const signUrl = `/sign/${recipientToken}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, envelope.recipients[0].id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - await verifyOriginalData(envelope.id, true); - }); - - test('[FILE_CONVERSION_API]: should upload and sign a PDF document via API (no conversion)', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - const pdfFile = fs.readFileSync(path.join(__dirname, '../../../../assets/example.pdf')); - - const formData = new FormData(); - - const createEnvelopePayload: TCreateEnvelopePayload = { - type: EnvelopeType.DOCUMENT, - title: 'PDF Upload Test', - recipients: [ - { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - fields: [ - { - type: FieldType.SIGNATURE, - fieldMeta: { type: 'signature' }, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - }, - ], - }, - ], - }; - - formData.append('payload', JSON.stringify(createEnvelopePayload)); - formData.append('files', new File([pdfFile], 'example.pdf', { type: 'application/pdf' })); - - const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, { - headers: { Authorization: `Bearer ${token}` }, - multipart: formData, - }); - - expect(createEnvelopeRequest.ok()).toBeTruthy(); - expect(createEnvelopeRequest.status()).toBe(200); - - const { id: createdEnvelopeId }: TCreateEnvelopeResponse = await createEnvelopeRequest.json(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: createdEnvelopeId }, - include: { - recipients: true, - envelopeItems: true, - }, - }); - - expect(envelope.envelopeItems.length).toBe(1); - expect(envelope.recipients.length).toBe(1); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - expect(distributeEnvelopeRequest.ok()).toBeTruthy(); - - const recipientToken = envelope.recipients[0].token; - const signUrl = `/sign/${recipientToken}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, envelope.recipients[0].id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - // PDF should NOT have originalData (no conversion needed) - await verifyOriginalData(envelope.id, false); - }); - - test('[FILE_CONVERSION_API]: should reject unsupported file types', async ({ request }) => { - const { user, team } = await seedUser(); - - const { token } = await createApiToken({ - userId: user.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - const jsonFile = fs.readFileSync(path.join(__dirname, '../../../../package.json')); - - const formData = new FormData(); - - const createEnvelopePayload: TCreateEnvelopePayload = { - type: EnvelopeType.DOCUMENT, - title: 'Unsupported File Test', - recipients: [ - { - email: user.email, - name: user.name || '', - role: RecipientRole.SIGNER, - fields: [ - { - type: FieldType.SIGNATURE, - fieldMeta: { type: 'signature' }, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - }, - ], - }, - ], - }; - - formData.append('payload', JSON.stringify(createEnvelopePayload)); - formData.append('files', new File([jsonFile], 'package.json', { type: 'application/json' })); - - const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, { - headers: { Authorization: `Bearer ${token}` }, - multipart: formData, - }); - - expect(createEnvelopeRequest.ok()).toBeFalsy(); - - const errorBody = await createEnvelopeRequest.json(); - // Accept either UNSUPPORTED_FILE_TYPE or INTERNAL_SERVER_ERROR as valid rejection - expect( - errorBody.code === 'UNSUPPORTED_FILE_TYPE' || errorBody.code === 'INTERNAL_SERVER_ERROR', - ).toBeTruthy(); - }); - - test('[FILE_CONVERSION_API]: should return error when Gotenberg is unavailable', async ({ - request, - }) => { - const { user, team } = await seedUser(); - - const { token } = await createApiToken({ - userId: user.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - const pngFile = fs.readFileSync(path.join(__dirname, '../../../../assets/test-image.png')); - - const formData = new FormData(); - - const createEnvelopePayload: TCreateEnvelopePayload = { - type: EnvelopeType.DOCUMENT, - title: 'Gotenberg Error Test', - recipients: [ - { - email: user.email, - name: user.name || '', - role: RecipientRole.SIGNER, - fields: [ - { - type: FieldType.SIGNATURE, - fieldMeta: { type: 'signature' }, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - }, - ], - }, - ], - }; - - formData.append('payload', JSON.stringify(createEnvelopePayload)); - formData.append('files', new File([pngFile], 'test-image.png', { type: 'image/png' })); - - const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, { - headers: { Authorization: `Bearer ${token}` }, - multipart: formData, - }); - - // This test only validates the error flow if Gotenberg is actually unavailable - if (createEnvelopeRequest.ok()) { - test.skip(true, 'Gotenberg is available - skipping error scenario test'); - return; - } - - const errorBody = await createEnvelopeRequest.json(); - const errorCode = - errorBody.code === 'INTERNAL_SERVER_ERROR' - ? errorBody.data?.code || errorBody.data?.appError?.code - : errorBody.code; - expect( - errorCode === 'CONVERSION_SERVICE_UNAVAILABLE' || errorCode === 'CONVERSION_FAILED', - ).toBeTruthy(); - }); - }); - - test.describe('UI Tests', () => { - test('[FILE_CONVERSION_UI]: should upload, convert, and sign a PNG image', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - await apiSignin({ - page, - email: uploader.email, - redirectPath: `/t/${team.url}/documents`, - }); - - // Wait for documents page to load - await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible(); - - // Upload PNG via file input - // Use .first() because there are two upload inputs (dropzone wrapper + upload button) - const fileInput = page.getByTestId('document-upload-input').first(); - await fileInput.setInputFiles(path.join(__dirname, '../../../../assets/test-image.png')); - - // Wait for redirect to editor or error toast - try { - await page.waitForURL(/\/documents\/.*\/edit/, { timeout: 15000 }); - } catch { - // Check if error toast appeared (Gotenberg unavailable) - const errorToast = page.locator('li.destructive'); - if (await errorToast.isVisible()) { - test.skip(true, 'Gotenberg conversion service is not available'); - return; - } - throw new Error('Upload failed without expected error toast'); - } - - // Get envelope ID from URL - const urlMatch = page.url().match(/documents\/(.+?)\/edit/); - const envelopeId = urlMatch?.[1]; - expect(envelopeId).toBeDefined(); - - // Add recipient via API - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: envelopeId }, - include: { envelopeItems: true }, - }); - - const recipientRecord = await prisma.recipient.create({ - data: { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - envelopeId: envelope.id, - token: nanoid(), - }, - }); - - // Add field via Prisma - await prisma.field.create({ - data: { - envelopeId: envelope.id, - recipientId: recipientRecord.id, - type: FieldType.SIGNATURE, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - envelopeItemId: envelope.envelopeItems[0].id, - customText: '', - inserted: false, - fieldMeta: { type: 'signature' }, - }, - }); - - // Distribute envelope via API - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - if (!distributeEnvelopeRequest.ok()) { - const errorBody = await distributeEnvelopeRequest.json(); - throw new Error(`Failed to distribute envelope: ${JSON.stringify(errorBody)}`); - } - - // Get updated recipient with token - const updatedRecipient = await prisma.recipient.findUniqueOrThrow({ - where: { id: recipientRecord.id }, - }); - - const signUrl = `/sign/${updatedRecipient.token}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, recipientRecord.id); - - // Sign as recipient - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - // Verify completion - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - // PNG should have originalData - await verifyOriginalData(envelope.id, true); - }); - - test('[FILE_CONVERSION_UI]: should upload, convert, and sign a JPEG image', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - await apiSignin({ - page, - email: uploader.email, - redirectPath: `/t/${team.url}/documents`, - }); - - await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible(); - - // Use .first() because there are two upload inputs (dropzone wrapper + upload button) - const fileInput = page.getByTestId('document-upload-input').first(); - await fileInput.setInputFiles(path.join(__dirname, '../../../../assets/test-image.jpg')); - - try { - await page.waitForURL(/\/documents\/.*\/edit/, { timeout: 15000 }); - } catch { - const errorToast = page.locator('li.destructive'); - if (await errorToast.isVisible()) { - test.skip(true, 'Gotenberg conversion service is not available'); - return; - } - throw new Error('Upload failed without expected error toast'); - } - - const urlMatch = page.url().match(/documents\/(.+?)\/edit/); - const envelopeId = urlMatch?.[1]; - expect(envelopeId).toBeDefined(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: envelopeId }, - include: { envelopeItems: true }, - }); - - const recipientRecord = await prisma.recipient.create({ - data: { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - envelopeId: envelope.id, - token: nanoid(), - }, - }); - - await prisma.field.create({ - data: { - envelopeId: envelope.id, - recipientId: recipientRecord.id, - type: FieldType.SIGNATURE, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - envelopeItemId: envelope.envelopeItems[0].id, - customText: '', - inserted: false, - fieldMeta: { type: 'signature' }, - }, - }); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - if (!distributeEnvelopeRequest.ok()) { - const errorBody = await distributeEnvelopeRequest.json(); - throw new Error(`Failed to distribute envelope: ${JSON.stringify(errorBody)}`); - } - - const updatedRecipient = await prisma.recipient.findUniqueOrThrow({ - where: { id: recipientRecord.id }, - }); - - const signUrl = `/sign/${updatedRecipient.token}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, recipientRecord.id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - await verifyOriginalData(envelope.id, true); - }); - - test('[FILE_CONVERSION_UI]: should upload, convert, and sign a DOCX document', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - await apiSignin({ - page, - email: uploader.email, - redirectPath: `/t/${team.url}/documents`, - }); - - await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible(); - - // Use .first() because there are two upload inputs (dropzone wrapper + upload button) - const fileInput = page.getByTestId('document-upload-input').first(); - await fileInput.setInputFiles(path.join(__dirname, '../../../../assets/test-document.docx')); - - try { - await page.waitForURL(/\/documents\/.*\/edit/, { timeout: 15000 }); - } catch { - const errorToast = page.locator('li.destructive'); - if (await errorToast.isVisible()) { - test.skip(true, 'Gotenberg conversion service is not available'); - return; - } - throw new Error('Upload failed without expected error toast'); - } - - const urlMatch = page.url().match(/documents\/(.+?)\/edit/); - const envelopeId = urlMatch?.[1]; - expect(envelopeId).toBeDefined(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: envelopeId }, - include: { envelopeItems: true }, - }); - - const recipientRecord = await prisma.recipient.create({ - data: { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - envelopeId: envelope.id, - token: nanoid(), - }, - }); - - await prisma.field.create({ - data: { - envelopeId: envelope.id, - recipientId: recipientRecord.id, - type: FieldType.SIGNATURE, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - envelopeItemId: envelope.envelopeItems[0].id, - customText: '', - inserted: false, - fieldMeta: { type: 'signature' }, - }, - }); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - if (!distributeEnvelopeRequest.ok()) { - const errorBody = await distributeEnvelopeRequest.json(); - throw new Error(`Failed to distribute envelope: ${JSON.stringify(errorBody)}`); - } - - const updatedRecipient = await prisma.recipient.findUniqueOrThrow({ - where: { id: recipientRecord.id }, - }); - - const signUrl = `/sign/${updatedRecipient.token}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, recipientRecord.id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - await verifyOriginalData(envelope.id, true); - }); - - test('[FILE_CONVERSION_UI]: should upload and sign a PDF document (no conversion)', async ({ - page, - request, - }) => { - const { user: uploader, team } = await seedUser(); - const { user: recipient } = await seedUser(); - - const { token } = await createApiToken({ - userId: uploader.id, - teamId: team.id, - tokenName: 'test', - expiresIn: null, - }); - - await apiSignin({ - page, - email: uploader.email, - redirectPath: `/t/${team.url}/documents`, - }); - - await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible(); - - // Use .first() because there are two upload inputs (dropzone wrapper + upload button) - const fileInput = page.getByTestId('document-upload-input').first(); - await fileInput.setInputFiles(path.join(__dirname, '../../../../assets/example.pdf')); - - await page.waitForURL(/\/documents\/.*\/edit/, { timeout: 15000 }); - - const urlMatch = page.url().match(/documents\/(.+?)\/edit/); - const envelopeId = urlMatch?.[1]; - expect(envelopeId).toBeDefined(); - - const envelope = await prisma.envelope.findUniqueOrThrow({ - where: { id: envelopeId }, - include: { envelopeItems: true }, - }); - - const recipientRecord = await prisma.recipient.create({ - data: { - email: recipient.email, - name: recipient.name || '', - role: RecipientRole.SIGNER, - envelopeId: envelope.id, - token: nanoid(), - }, - }); - - await prisma.field.create({ - data: { - envelopeId: envelope.id, - recipientId: recipientRecord.id, - type: FieldType.SIGNATURE, - page: 1, - positionX: 10, - positionY: 10, - width: 20, - height: 5, - envelopeItemId: envelope.envelopeItems[0].id, - customText: '', - inserted: false, - fieldMeta: { type: 'signature' }, - }, - }); - - const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, { - headers: { Authorization: `Bearer ${token}` }, - data: { - envelopeId: envelope.id, - } satisfies TDistributeEnvelopeRequest, - }); - - if (!distributeEnvelopeRequest.ok()) { - const errorBody = await distributeEnvelopeRequest.json(); - throw new Error(`Failed to distribute envelope: ${JSON.stringify(errorBody)}`); - } - - const updatedRecipient = await prisma.recipient.findUniqueOrThrow({ - where: { id: recipientRecord.id }, - }); - - const signUrl = `/sign/${updatedRecipient.token}`; - - // Mark signature fields as inserted via Prisma BEFORE loading page - // (bypasses Konva canvas interaction) - await markSignatureFieldsAsInserted(envelope.id, recipientRecord.id); - - await apiSignin({ - page, - email: recipient.email, - redirectPath: signUrl, - }); - - await completeSigningFlow(page); - - await expect(async () => { - const { status } = await prisma.envelope.findFirstOrThrow({ - where: { id: envelope.id }, - }); - expect(status).toBe(DocumentStatus.COMPLETED); - }).toPass({ timeout: 10000 }); - - // PDF should NOT have originalData - await verifyOriginalData(envelope.id, false); - }); - - test('[FILE_CONVERSION_UI]: should show error when Gotenberg is unavailable', async ({ - page, - }) => { - const { user, team } = await seedUser(); - - await apiSignin({ - page, - email: user.email, - redirectPath: `/t/${team.url}/documents`, - }); - - await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible(); - - // Use .first() because there are two upload inputs (dropzone wrapper + upload button) - const fileInput = page.getByTestId('document-upload-input').first(); - await fileInput.setInputFiles(path.join(__dirname, '../../../../assets/test-image.png')); - - // Wait for either navigation (success) or error toast (Gotenberg down) - const navigationPromise = page.waitForURL(/\/documents\/.*\/edit/, { timeout: 15000 }); - const toastPromise = page.waitForSelector('li.destructive', { - timeout: 15000, - }); - - const result = await Promise.race([ - navigationPromise.then(() => 'navigation'), - toastPromise.then(() => 'toast'), - ]).catch(() => 'timeout'); - - if (result === 'navigation') { - test.skip(true, 'Gotenberg is available - skipping error scenario test'); - return; - } - - if (result === 'toast') { - // Error toast appeared - Gotenberg is unavailable - const errorToast = page.locator('li.destructive'); - await expect(errorToast).toBeVisible(); - } else { - throw new Error('Neither navigation nor error toast occurred'); - } - }); - }); -});