diff --git a/apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx b/apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx index 1240a0dc0..7f6bee7bb 100644 --- a/apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx +++ b/apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx @@ -1,3 +1,5 @@ +import { useState } from 'react'; + import { zodResolver } from '@hookform/resolvers/zod'; import { Trans } from '@lingui/react/macro'; import { useForm } from 'react-hook-form'; @@ -55,6 +57,8 @@ export function AssistantConfirmationDialog({ allowDictateNextSigner = false, defaultNextSigner, }: ConfirmationDialogProps) { + const [isEditingNextSigner, setIsEditingNextSigner] = useState(false); + const form = useForm({ resolver: zodResolver(ZNextSignerFormSchema), defaultValues: { @@ -107,53 +111,72 @@ export function AssistantConfirmationDialog({
{allowDictateNextSigner && ( -
-

- The next recipient to sign this document will be{' '} -

+
+ {!isEditingNextSigner && ( +
+

+ The next recipient to sign this document will be{' '} + {form.watch('name')} ( + {form.watch('email')}). +

-
- ( - - - Name - - - - - - - )} - /> + +
+ )} - ( - - - Email - - - - - - - )} - /> -
+ {isEditingNextSigner && ( +
+ ( + + + Name + + + + + + + + )} + /> + + ( + + + Email + + + + + + + )} + /> +
+ )}
)} diff --git a/packages/app-tests/e2e/document-auth/access-auth.spec.ts b/packages/app-tests/e2e/document-auth/access-auth.spec.ts index 8fb4afc04..a84eacf58 100644 --- a/packages/app-tests/e2e/document-auth/access-auth.spec.ts +++ b/packages/app-tests/e2e/document-auth/access-auth.spec.ts @@ -7,8 +7,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[DOCUMENT_AUTH]: should grant access when not required', async ({ page }) => { const user = await seedUser(); diff --git a/packages/app-tests/e2e/document-auth/next-recipient-dictation.spec.ts b/packages/app-tests/e2e/document-auth/next-recipient-dictation.spec.ts index 3f3b47724..3130404f5 100644 --- a/packages/app-tests/e2e/document-auth/next-recipient-dictation.spec.ts +++ b/packages/app-tests/e2e/document-auth/next-recipient-dictation.spec.ts @@ -377,8 +377,8 @@ test('[NEXT_RECIPIENT_DICTATION]: should allow assistant to dictate next signer' // Second recipient should be the new signer const updatedSigner = updatedDocument.recipients[1]; - expect(updatedSigner.name).toBe('New Signer'); - expect(updatedSigner.email).toBe('new.signer@example.com'); + expect(updatedSigner.name).toBe('New Recipient'); + expect(updatedSigner.email).toBe('new.recipient@example.com'); expect(updatedSigner.signingOrder).toBe(2); expect(updatedSigner.signingStatus).toBe(SigningStatus.NOT_SIGNED); expect(updatedSigner.role).toBe(RecipientRole.SIGNER); diff --git a/packages/app-tests/e2e/document-flow/settings-step.spec.ts b/packages/app-tests/e2e/document-flow/settings-step.spec.ts index 10860db21..e375f4d45 100644 --- a/packages/app-tests/e2e/document-flow/settings-step.spec.ts +++ b/packages/app-tests/e2e/document-flow/settings-step.spec.ts @@ -11,9 +11,8 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test.describe('[EE_ONLY]', () => { + // eslint-disable-next-line turbo/no-undeclared-env-vars const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || ''; test.beforeEach(() => { diff --git a/packages/app-tests/e2e/document-flow/signers-step.spec.ts b/packages/app-tests/e2e/document-flow/signers-step.spec.ts index bf64eb735..22a8b2399 100644 --- a/packages/app-tests/e2e/document-flow/signers-step.spec.ts +++ b/packages/app-tests/e2e/document-flow/signers-step.spec.ts @@ -6,9 +6,8 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test.describe('[EE_ONLY]', () => { + // eslint-disable-next-line turbo/no-undeclared-env-vars const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || ''; test.beforeEach(() => { diff --git a/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts b/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts index 448c70cda..52f203c98 100644 --- a/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts +++ b/packages/app-tests/e2e/public-profiles/public-profiles.spec.ts @@ -7,8 +7,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[PUBLIC_PROFILE]: create profile', async ({ page }) => { const user = await seedUser(); diff --git a/packages/app-tests/e2e/teams/manage-team.spec.ts b/packages/app-tests/e2e/teams/manage-team.spec.ts index 95bae9df5..d226030ad 100644 --- a/packages/app-tests/e2e/teams/manage-team.spec.ts +++ b/packages/app-tests/e2e/teams/manage-team.spec.ts @@ -6,8 +6,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: create team', async ({ page }) => { const user = await seedUser(); diff --git a/packages/app-tests/e2e/teams/team-documents.spec.ts b/packages/app-tests/e2e/teams/team-documents.spec.ts index bdd9d1dc0..777e84b3b 100644 --- a/packages/app-tests/e2e/teams/team-documents.spec.ts +++ b/packages/app-tests/e2e/teams/team-documents.spec.ts @@ -9,8 +9,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin, apiSignout } from '../fixtures/authentication'; import { checkDocumentTabCount } from '../fixtures/documents'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: check team documents count', async ({ page }) => { const { team, teamMember2 } = await seedTeamDocuments(); diff --git a/packages/app-tests/e2e/teams/team-email.spec.ts b/packages/app-tests/e2e/teams/team-email.spec.ts index ea749891e..06d5beb44 100644 --- a/packages/app-tests/e2e/teams/team-email.spec.ts +++ b/packages/app-tests/e2e/teams/team-email.spec.ts @@ -6,8 +6,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: send team email request', async ({ page }) => { const team = await seedTeam(); diff --git a/packages/app-tests/e2e/teams/team-global-settings.spec.ts b/packages/app-tests/e2e/teams/team-global-settings.spec.ts index 34b3f412e..7e277e36b 100644 --- a/packages/app-tests/e2e/teams/team-global-settings.spec.ts +++ b/packages/app-tests/e2e/teams/team-global-settings.spec.ts @@ -4,8 +4,6 @@ import { seedTeam } from '@documenso/prisma/seed/teams'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: update the default document visibility in the team global settings', async ({ page, }) => { diff --git a/packages/app-tests/e2e/teams/team-members.spec.ts b/packages/app-tests/e2e/teams/team-members.spec.ts index 3bf9387d4..44dd30901 100644 --- a/packages/app-tests/e2e/teams/team-members.spec.ts +++ b/packages/app-tests/e2e/teams/team-members.spec.ts @@ -6,8 +6,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: update team member role', async ({ page }) => { const team = await seedTeam({ createTeamMembers: 1, diff --git a/packages/app-tests/e2e/teams/team-signature-settings.spec.ts b/packages/app-tests/e2e/teams/team-signature-settings.spec.ts index 61a85bb42..1528f3732 100644 --- a/packages/app-tests/e2e/teams/team-signature-settings.spec.ts +++ b/packages/app-tests/e2e/teams/team-signature-settings.spec.ts @@ -9,8 +9,6 @@ import { import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: check that default team signature settings are all enabled', async ({ page }) => { const { team } = await seedTeamDocuments(); @@ -149,8 +147,9 @@ test('[TEAMS]: check signature modes work for templates', async ({ page }) => { await page.getByRole('button', { name: 'Update' }).first().click(); // Wait for finish - await page.getByText('Document preferences updated').waitFor({ state: 'visible' }); - await page.waitForTimeout(1000); + const toast = page.locator('li[role="status"][data-state="open"]').first(); + await expect(toast).toBeVisible(); + await expect(toast.getByText('Document preferences updated', { exact: true })).toBeVisible(); const template = await seedTeamTemplateWithMeta(team); diff --git a/packages/app-tests/e2e/teams/transfer-team.spec.ts b/packages/app-tests/e2e/teams/transfer-team.spec.ts index 0dfe2d736..006bccec5 100644 --- a/packages/app-tests/e2e/teams/transfer-team.spec.ts +++ b/packages/app-tests/e2e/teams/transfer-team.spec.ts @@ -5,8 +5,6 @@ import { seedTeam, seedTeamTransfer } from '@documenso/prisma/seed/teams'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEAMS]: initiate and cancel team transfer', async ({ page }) => { const team = await seedTeam({ createTeamMembers: 1, diff --git a/packages/app-tests/e2e/templates-flow/template-settings-step.spec.ts b/packages/app-tests/e2e/templates-flow/template-settings-step.spec.ts index 980c83276..6cd714082 100644 --- a/packages/app-tests/e2e/templates-flow/template-settings-step.spec.ts +++ b/packages/app-tests/e2e/templates-flow/template-settings-step.spec.ts @@ -9,8 +9,6 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test.describe('[EE_ONLY]', () => { const enterprisePriceId = ''; diff --git a/packages/app-tests/e2e/templates-flow/template-signers-step.spec.ts b/packages/app-tests/e2e/templates-flow/template-signers-step.spec.ts index 3ce52c55c..fe3efeb83 100644 --- a/packages/app-tests/e2e/templates-flow/template-signers-step.spec.ts +++ b/packages/app-tests/e2e/templates-flow/template-signers-step.spec.ts @@ -6,9 +6,8 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test.describe('[EE_ONLY]', () => { + // eslint-disable-next-line turbo/no-undeclared-env-vars const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || ''; test.beforeEach(() => { diff --git a/packages/app-tests/e2e/templates/create-document-from-template.spec.ts b/packages/app-tests/e2e/templates/create-document-from-template.spec.ts index 695de51c0..8add68d7d 100644 --- a/packages/app-tests/e2e/templates/create-document-from-template.spec.ts +++ b/packages/app-tests/e2e/templates/create-document-from-template.spec.ts @@ -1,7 +1,6 @@ import { expect, test } from '@playwright/test'; import { DocumentDataType, TeamMemberRole } from '@prisma/client'; import fs from 'fs'; -import os from 'os'; import path from 'path'; import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth'; @@ -13,23 +12,9 @@ import { seedUser } from '@documenso/prisma/seed/users'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - const enterprisePriceId = ''; -// Create a temporary PDF file for testing -function createTempPdfFile() { - const tempDir = os.tmpdir(); - const tempFilePath = path.join(tempDir, 'test.pdf'); - - // Create a simple PDF file with some content - const pdfContent = Buffer.from( - '%PDF-1.4\n1 0 obj<>endobj 2 0 obj<>endobj 3 0 obj<>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\n0000000101 00000 n\ntrailer<>\nstartxref\n178\n%%EOF', - ); - - fs.writeFileSync(tempFilePath, new Uint8Array(pdfContent)); - return tempFilePath; -} +const EXAMPLE_PDF_PATH = path.join(__dirname, '../../../../assets/example.pdf'); /** * 1. Create a template with all settings filled out @@ -313,67 +298,73 @@ test('[TEMPLATE]: should create a document from a template with custom document' const template = await seedBlankTemplate(user); // Create a temporary PDF file for upload - const testPdfPath = createTempPdfFile(); - const pdfContent = fs.readFileSync(testPdfPath).toString('base64'); - try { - await apiSignin({ - page, - email: user.email, - redirectPath: `/templates/${template.id}/edit`, - }); + const pdfContent = fs.readFileSync(EXAMPLE_PDF_PATH).toString('base64'); - // Set template title - await page.getByLabel('Title').fill('TEMPLATE_WITH_CUSTOM_DOC'); + await apiSignin({ + page, + email: user.email, + redirectPath: `/templates/${template.id}/edit`, + }); - await page.getByRole('button', { name: 'Continue' }).click(); - await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); + // Set template title + await page.getByLabel('Title').fill('TEMPLATE_WITH_CUSTOM_DOC'); - // Add a signer - await page.getByPlaceholder('Email').fill('recipient@documenso.com'); - await page.getByPlaceholder('Name').fill('Recipient'); + await page.getByRole('button', { name: 'Continue' }).click(); + await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); - await page.getByRole('button', { name: 'Continue' }).click(); - await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible(); + // Add a signer + await page.getByPlaceholder('Email').fill('recipient@documenso.com'); + await page.getByPlaceholder('Name').fill('Recipient'); - await page.getByRole('button', { name: 'Save template' }).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible(); - // Use template with custom document - await page.waitForURL('/templates'); - await page.getByRole('button', { name: 'Use Template' }).click(); + await page.getByRole('button', { name: 'Save template' }).click(); - // Enable custom document upload and upload file - await page.getByLabel('Upload custom document').check(); - await page.locator('input[type="file"]').setInputFiles(testPdfPath); + // Use template with custom document + await page.waitForURL('/templates'); + await page.getByRole('button', { name: 'Use Template' }).click(); - // Wait for upload to complete - await expect(page.getByText(path.basename(testPdfPath))).toBeVisible(); + // Enable custom document upload and upload file + await page.getByLabel('Upload custom document').check(); - // Create document with custom document data - await page.getByRole('button', { name: 'Create as draft' }).click(); + // Upload document. + const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.locator('input[type=file]').evaluate((e) => { + if (e instanceof HTMLInputElement) { + e.click(); + } + }), + ]); - // Review that the document was created with the custom document data - await page.waitForURL(/documents/); + await fileChooser.setFiles(EXAMPLE_PDF_PATH); - const documentId = Number(page.url().split('/').pop()); + // Wait for upload to complete + await expect(page.getByText(path.basename(EXAMPLE_PDF_PATH))).toBeVisible(); - const document = await prisma.document.findFirstOrThrow({ - where: { - id: documentId, - }, - include: { - documentData: true, - }, - }); + // Create document with custom document data + await page.getByRole('button', { name: 'Create as draft' }).click(); - expect(document.title).toEqual('TEMPLATE_WITH_CUSTOM_DOC'); - expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64); - expect(document.documentData.data).toEqual(pdfContent); - expect(document.documentData.initialData).toEqual(pdfContent); - } finally { - // Clean up the temporary file - fs.unlinkSync(testPdfPath); - } + // Review that the document was created with the custom document data + await page.waitForURL(/documents/); + + const documentId = Number(page.url().split('/').pop()); + + const document = await prisma.document.findFirstOrThrow({ + where: { + id: documentId, + }, + include: { + documentData: true, + }, + }); + + expect(document.title).toEqual('TEMPLATE_WITH_CUSTOM_DOC'); + expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64); + expect(document.documentData.data).toEqual(pdfContent); + expect(document.documentData.initialData).toEqual(pdfContent); }); /** @@ -393,69 +384,73 @@ test('[TEMPLATE]: should create a team document from a template with custom docu }, }); - // Create a temporary PDF file for upload - const testPdfPath = createTempPdfFile(); - const pdfContent = fs.readFileSync(testPdfPath).toString('base64'); + const pdfContent = fs.readFileSync(EXAMPLE_PDF_PATH).toString('base64'); - try { - await apiSignin({ - page, - email: owner.email, - redirectPath: `/t/${team.url}/templates/${template.id}/edit`, - }); + await apiSignin({ + page, + email: owner.email, + redirectPath: `/t/${team.url}/templates/${template.id}/edit`, + }); - // Set template title - await page.getByLabel('Title').fill('TEAM_TEMPLATE_WITH_CUSTOM_DOC'); + // Set template title + await page.getByLabel('Title').fill('TEAM_TEMPLATE_WITH_CUSTOM_DOC'); - await page.getByRole('button', { name: 'Continue' }).click(); - await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); + await page.getByRole('button', { name: 'Continue' }).click(); + await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); - // Add a signer - await page.getByPlaceholder('Email').fill('recipient@documenso.com'); - await page.getByPlaceholder('Name').fill('Recipient'); + // Add a signer + await page.getByPlaceholder('Email').fill('recipient@documenso.com'); + await page.getByPlaceholder('Name').fill('Recipient'); - await page.getByRole('button', { name: 'Continue' }).click(); - await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible(); + await page.getByRole('button', { name: 'Continue' }).click(); + await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible(); - await page.getByRole('button', { name: 'Save template' }).click(); + await page.getByRole('button', { name: 'Save template' }).click(); - // Use template with custom document - await page.waitForURL(`/t/${team.url}/templates`); - await page.getByRole('button', { name: 'Use Template' }).click(); + // Use template with custom document + await page.waitForURL(`/t/${team.url}/templates`); + await page.getByRole('button', { name: 'Use Template' }).click(); - // Enable custom document upload and upload file - await page.getByLabel('Upload custom document').check(); - await page.locator('input[type="file"]').setInputFiles(testPdfPath); + // Enable custom document upload and upload file + await page.getByLabel('Upload custom document').check(); - // Wait for upload to complete - await expect(page.getByText(path.basename(testPdfPath))).toBeVisible(); + // Upload document. + const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.locator('input[type=file]').evaluate((e) => { + if (e instanceof HTMLInputElement) { + e.click(); + } + }), + ]); - // Create document with custom document data - await page.getByRole('button', { name: 'Create as draft' }).click(); + await fileChooser.setFiles(EXAMPLE_PDF_PATH); - // Review that the document was created with the custom document data - await page.waitForURL(/documents/); + // Wait for upload to complete + await expect(page.getByText(path.basename(EXAMPLE_PDF_PATH))).toBeVisible(); - const documentId = Number(page.url().split('/').pop()); + // Create document with custom document data + await page.getByRole('button', { name: 'Create as draft' }).click(); - const document = await prisma.document.findFirstOrThrow({ - where: { - id: documentId, - }, - include: { - documentData: true, - }, - }); + // Review that the document was created with the custom document data + await page.waitForURL(/documents/); - expect(document.teamId).toEqual(team.id); - expect(document.title).toEqual('TEAM_TEMPLATE_WITH_CUSTOM_DOC'); - expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64); - expect(document.documentData.data).toEqual(pdfContent); - expect(document.documentData.initialData).toEqual(pdfContent); - } finally { - // Clean up the temporary file - fs.unlinkSync(testPdfPath); - } + const documentId = Number(page.url().split('/').pop()); + + const document = await prisma.document.findFirstOrThrow({ + where: { + id: documentId, + }, + include: { + documentData: true, + }, + }); + + expect(document.teamId).toEqual(team.id); + expect(document.title).toEqual('TEAM_TEMPLATE_WITH_CUSTOM_DOC'); + expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64); + expect(document.documentData.data).toEqual(pdfContent); + expect(document.documentData.initialData).toEqual(pdfContent); }); /** diff --git a/packages/app-tests/e2e/templates/direct-templates.spec.ts b/packages/app-tests/e2e/templates/direct-templates.spec.ts index 2a44acd6a..aa4730395 100644 --- a/packages/app-tests/e2e/templates/direct-templates.spec.ts +++ b/packages/app-tests/e2e/templates/direct-templates.spec.ts @@ -23,8 +23,6 @@ const formatTemplatesPath = (teamUrl?: string) => const nanoid = customAlphabet('1234567890abcdef', 10); -test.describe.configure({ mode: 'parallel' }); - test('[DIRECT_TEMPLATES]: create direct link for template', async ({ page }) => { const team = await seedTeam({ createTeamMembers: 1, diff --git a/packages/app-tests/e2e/templates/manage-templates.spec.ts b/packages/app-tests/e2e/templates/manage-templates.spec.ts index f0d1bdc9a..3c0f2fef6 100644 --- a/packages/app-tests/e2e/templates/manage-templates.spec.ts +++ b/packages/app-tests/e2e/templates/manage-templates.spec.ts @@ -6,8 +6,6 @@ import { seedTemplate } from '@documenso/prisma/seed/templates'; import { apiSignin } from '../fixtures/authentication'; -test.describe.configure({ mode: 'parallel' }); - test('[TEMPLATES]: view templates', async ({ page }) => { const team = await seedTeam({ createTeamMembers: 1,