chore: merge main, resolve biome formatting conflicts

Merge origin/main into feat/external-2fa-codes. Resolve formatting
conflicts caused by biome rollout; preserve both feature streams:
PR's external 2FA token + signing-session 2FA proof additions plus
main's RateLimit/RecipientExpired/signingReminders/date-auto-insert.

In complete-document-with-token.ts, drop the duplicate early
field-fetching block introduced when main moved that logic later
with date auto-insert support; keep the EXTERNAL_TWO_FACTOR_AUTH
check using derivedRecipientActionAuth.
This commit is contained in:
ephraimduncan
2026-05-12 11:46:11 +00:00
parent 9194884fbe
commit 138d663c25
1959 changed files with 93488 additions and 47038 deletions
@@ -1,9 +1,9 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { prisma } from '@documenso/prisma';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { apiSignin } from '../fixtures/authentication';
@@ -46,7 +46,7 @@ test.describe('AutoSave Fields Step', () => {
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -54,7 +54,7 @@ test.describe('AutoSave Fields Step', () => {
});
await page.getByRole('button', { name: 'Text' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -63,10 +63,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByTestId('field-advanced-settings-footer').waitFor({ state: 'visible' });
await page
.getByTestId('field-advanced-settings-footer')
.getByRole('button', { name: 'Cancel' })
.click();
await page.getByTestId('field-advanced-settings-footer').getByRole('button', { name: 'Cancel' }).click();
await triggerAutosave(page);
@@ -74,7 +71,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByRole('option', { name: 'Recipient 2 (recipient2@documenso.com)' }).click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 500,
@@ -100,7 +97,7 @@ test.describe('AutoSave Fields Step', () => {
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -108,7 +105,7 @@ test.describe('AutoSave Fields Step', () => {
});
await page.getByRole('button', { name: 'Text' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -117,10 +114,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByTestId('field-advanced-settings-footer').waitFor({ state: 'visible' });
await page
.getByTestId('field-advanced-settings-footer')
.getByRole('button', { name: 'Cancel' })
.click();
await page.getByTestId('field-advanced-settings-footer').getByRole('button', { name: 'Cancel' }).click();
await triggerAutosave(page);
@@ -128,7 +122,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByRole('option', { name: 'Recipient 2 (recipient2@documenso.com)' }).click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 500,
@@ -162,7 +156,7 @@ test.describe('AutoSave Fields Step', () => {
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -170,7 +164,7 @@ test.describe('AutoSave Fields Step', () => {
});
await page.getByRole('button', { name: 'Text' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -179,10 +173,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByTestId('field-advanced-settings-footer').waitFor({ state: 'visible' });
await page
.getByTestId('field-advanced-settings-footer')
.getByRole('button', { name: 'Cancel' })
.click();
await page.getByTestId('field-advanced-settings-footer').getByRole('button', { name: 'Cancel' }).click();
await triggerAutosave(page);
@@ -190,7 +181,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByRole('option', { name: 'Recipient 2 (recipient2@documenso.com)' }).click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 500,
@@ -224,7 +215,7 @@ test.describe('AutoSave Fields Step', () => {
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -232,7 +223,7 @@ test.describe('AutoSave Fields Step', () => {
});
await page.getByRole('button', { name: 'Text' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -243,10 +234,7 @@ test.describe('AutoSave Fields Step', () => {
await page.getByRole('textbox', { name: 'Field placeholder' }).fill('Test Placeholder');
await page.getByRole('textbox', { name: 'Add text to the field' }).fill('Test Text');
await page
.getByTestId('field-advanced-settings-footer')
.getByRole('button', { name: 'Save' })
.click();
await page.getByTestId('field-advanced-settings-footer').getByRole('button', { name: 'Save' }).click();
await triggerAutosave(page);
@@ -268,11 +256,7 @@ test.describe('AutoSave Fields Step', () => {
expect(textField.fieldMeta).toBeDefined();
if (
textField.fieldMeta &&
typeof textField.fieldMeta === 'object' &&
'type' in textField.fieldMeta
) {
if (textField.fieldMeta && typeof textField.fieldMeta === 'object' && 'type' in textField.fieldMeta) {
expect(textField.fieldMeta.type).toBe('text');
expect(textField.fieldMeta.label).toBe('Test Field');
expect(textField.fieldMeta.placeholder).toBe('Test Placeholder');
@@ -1,10 +1,9 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { apiSignin } from '../fixtures/authentication';
@@ -1,12 +1,11 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { apiSignin } from '../fixtures/authentication';
@@ -167,15 +166,9 @@ test.describe('AutoSave Signers Step', () => {
expect(retrievedDocumentData.documentMeta?.allowDictateNextSigner).toBe(true);
expect(retrievedRecipients.length).toBe(3);
const firstRecipient = retrievedRecipients.find(
(r) => r.email === 'recipient1@documenso.com',
);
const secondRecipient = retrievedRecipients.find(
(r) => r.email === 'recipient2@documenso.com',
);
const thirdRecipient = retrievedRecipients.find(
(r) => r.email === 'recipient3@documenso.com',
);
const firstRecipient = retrievedRecipients.find((r) => r.email === 'recipient1@documenso.com');
const secondRecipient = retrievedRecipients.find((r) => r.email === 'recipient2@documenso.com');
const thirdRecipient = retrievedRecipients.find((r) => r.email === 'recipient3@documenso.com');
expect(firstRecipient?.signingOrder).toBe(2);
expect(secondRecipient?.signingOrder).toBe(3);
@@ -1,10 +1,10 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { EnvelopeType } from '@prisma/client';
import { apiSignin } from '../fixtures/authentication';
@@ -28,7 +28,7 @@ export const setupDocumentAndNavigateToSubjectStep = async (page: Page) => {
await page.getByRole('button', { name: 'Continue' }).click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -106,10 +106,10 @@ test.describe('AutoSave Subject Step', () => {
const { user, document, team } = await setupDocumentAndNavigateToSubjectStep(page);
// Toggle some email settings checkboxes (randomly - some checked, some unchecked)
await page.getByText('Send recipient signed email').click();
await page.getByText('Send recipient removed email').click();
await page.getByText('Send document completed email', { exact: true }).click();
await page.getByText('Send document deleted email').click();
await page.getByText('Email the owner when a recipient signs').click();
await page.getByText("Email recipients when they're removed from a pending document").click();
await page.getByText('Email recipients when the document is completed', { exact: true }).click();
await page.getByText('Email recipients when a pending document is deleted').click();
await triggerAutosave(page);
@@ -126,26 +126,26 @@ test.describe('AutoSave Subject Step', () => {
const emailSettings = retrievedDocumentData.documentMeta?.emailSettings;
await expect(page.getByText('Send recipient signed email')).toBeChecked({
await expect(page.getByText('Email the owner when a recipient signs')).toBeChecked({
checked: emailSettings?.recipientSigned,
});
await expect(page.getByText('Send recipient removed email')).toBeChecked({
await expect(page.getByText("Email recipients when they're removed from a pending document")).toBeChecked({
checked: emailSettings?.recipientRemoved,
});
await expect(page.getByText('Send document completed email', { exact: true })).toBeChecked({
await expect(page.getByText('Email recipients when the document is completed', { exact: true })).toBeChecked({
checked: emailSettings?.documentCompleted,
});
await expect(page.getByText('Send document deleted email')).toBeChecked({
await expect(page.getByText('Email recipients when a pending document is deleted')).toBeChecked({
checked: emailSettings?.documentDeleted,
});
await expect(page.getByText('Send recipient signing request email')).toBeChecked({
await expect(page.getByText('Email recipients with a signing request')).toBeChecked({
checked: emailSettings?.recipientSigningRequest,
});
await expect(page.getByText('Send document pending email')).toBeChecked({
await expect(page.getByText('Email the signer if the document is still pending')).toBeChecked({
checked: emailSettings?.documentPending,
});
await expect(page.getByText('Send document completed email to the owner')).toBeChecked({
await expect(page.getByText('Email the owner when the document is completed')).toBeChecked({
checked: emailSettings?.ownerDocumentCompleted,
});
}).toPass();
@@ -161,10 +161,10 @@ test.describe('AutoSave Subject Step', () => {
await page.getByRole('textbox', { name: 'Subject (Optional)' }).fill(subject);
await page.getByRole('textbox', { name: 'Message (Optional)' }).fill(message);
await page.getByText('Send recipient signed email').click();
await page.getByText('Send recipient removed email').click();
await page.getByText('Send document completed email', { exact: true }).click();
await page.getByText('Send document deleted email').click();
await page.getByText('Email the owner when a recipient signs').click();
await page.getByText("Email recipients when they're removed from a pending document").click();
await page.getByText('Email recipients when the document is completed', { exact: true }).click();
await page.getByText('Email recipients when a pending document is deleted').click();
await triggerAutosave(page);
@@ -190,26 +190,26 @@ test.describe('AutoSave Subject Step', () => {
retrievedDocumentData.documentMeta?.message ?? '',
);
await expect(page.getByText('Send recipient signed email')).toBeChecked({
await expect(page.getByText('Email the owner when a recipient signs')).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.recipientSigned,
});
await expect(page.getByText('Send recipient removed email')).toBeChecked({
await expect(page.getByText("Email recipients when they're removed from a pending document")).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.recipientRemoved,
});
await expect(page.getByText('Send document completed email', { exact: true })).toBeChecked({
await expect(page.getByText('Email recipients when the document is completed', { exact: true })).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.documentCompleted,
});
await expect(page.getByText('Send document deleted email')).toBeChecked({
await expect(page.getByText('Email recipients when a pending document is deleted')).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.documentDeleted,
});
await expect(page.getByText('Send recipient signing request email')).toBeChecked({
await expect(page.getByText('Email recipients with a signing request')).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.recipientSigningRequest,
});
await expect(page.getByText('Send document pending email')).toBeChecked({
await expect(page.getByText('Email the signer if the document is still pending')).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.documentPending,
});
await expect(page.getByText('Send document completed email to the owner')).toBeChecked({
await expect(page.getByText('Email the owner when the document is completed')).toBeChecked({
checked: retrievedDocumentData.documentMeta?.emailSettings?.ownerDocumentCompleted,
});
}).toPass();
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';
import { apiSignin } from '../fixtures/authentication';
@@ -33,14 +33,14 @@ test('[DOCUMENT_FLOW]: Simple duplicate recipients test', async ({ page }) => {
// Step 3: Add fields
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 100, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });
await page.getByRole('combobox').first().click();
// Switch to second duplicate and add field
await page.getByText('Duplicate 2 (duplicate@example.com)').first().click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 200, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });
// Continue to send
await page.getByRole('button', { name: 'Continue' }).click();
@@ -1,10 +1,9 @@
import { type Page, expect, test } from '@playwright/test';
import type { Document, Team } from '@prisma/client';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { prisma } from '@documenso/prisma';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, type Page, test } from '@playwright/test';
import type { Document, Team } from '@prisma/client';
import { apiSignin } from '../fixtures/authentication';
import { signSignaturePad } from '../fixtures/signature';
@@ -12,11 +11,7 @@ import { signSignaturePad } from '../fixtures/signature';
/**
* Test helper to complete the document creation flow with duplicate recipients
*/
const completeDocumentFlowWithDuplicateRecipients = async (options: {
page: Page;
team: Team;
document: Document;
}) => {
const completeDocumentFlowWithDuplicateRecipients = async (options: { page: Page; team: Team; document: Document }) => {
const { page, team, document } = options;
// Step 1: Settings - Continue with defaults
@@ -44,21 +39,21 @@ const completeDocumentFlowWithDuplicateRecipients = async (options: {
// Step 3: Add fields for each recipient
// Add signature field for first duplicate recipient
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 100, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });
await page.getByText('Duplicate Recipient 1 (duplicate@example.com)').click();
// Switch to second duplicate recipient and add their field
await page.getByText('Duplicate Recipient 2 (duplicate@example.com)').click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 200, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });
await page.getByText('Duplicate Recipient 2 (duplicate@example.com)').click();
// Switch to unique recipient and add their field
await page.getByText('Unique Recipient (unique@example.com)').click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 300, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 300, y: 100 } });
// Continue to subject
await page.getByRole('button', { name: 'Continue' }).click();
@@ -97,9 +92,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await expect(page).toHaveURL(new RegExp(`/t/${team.url}/documents`));
});
test('should allow adding duplicate recipient after saving document initially', async ({
page,
}) => {
test('should allow adding duplicate recipient after saving document initially', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
@@ -122,7 +115,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 100, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });
// Save the document by going to subject
await page.getByRole('button', { name: 'Continue' }).click();
@@ -149,7 +142,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await page.getByText('Test Recipient Duplicate (test@example.com)').first().click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 200, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });
// Complete the flow
await page.getByRole('button', { name: 'Continue' }).click();
@@ -162,10 +155,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await expect(page.getByRole('link', { name: document.title })).toBeVisible();
});
test('should isolate fields per recipient token even with duplicate emails', async ({
page,
context,
}) => {
test('should isolate fields per recipient token even with duplicate emails', async ({ page, context }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
@@ -208,9 +198,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await page.waitForSelector(PDF_VIEWER_PAGE_SELECTOR);
// Verify only one signature field is visible for this recipient
expect(
await page.locator(`[data-field-type="SIGNATURE"]:not([data-readonly="true"])`).all(),
).toHaveLength(1);
expect(await page.locator(`[data-field-type="SIGNATURE"]:not([data-readonly="true"])`).all()).toHaveLength(1);
// Verify recipient name is correct
await expect(page.getByLabel('Full Name')).toHaveValue(recipient.name);
@@ -218,10 +206,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
// Sign the document
await signSignaturePad(page);
await page
.locator('[data-field-type="SIGNATURE"]:not([data-readonly="true"])')
.first()
.click();
await page.locator('[data-field-type="SIGNATURE"]:not([data-readonly="true"])').first().click();
await page.getByRole('button', { name: 'Complete' }).click();
await page.getByRole('button', { name: 'Sign' }).click();
@@ -232,9 +217,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
}
});
test('should handle duplicate recipient workflow with different field types', async ({
page,
}) => {
test('should handle duplicate recipient workflow with different field types', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
@@ -270,24 +253,24 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
// Add signature for first recipient
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 100, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });
// Add name field for second recipient
await page.getByRole('combobox').first().click();
await page.getByText('Approver Role (signer@example.com)').first().click();
await page.getByRole('button', { name: 'Name' }).click();
await page.locator('canvas').click({ position: { x: 200, y: 100 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });
// Add date field for second recipient
await page.getByRole('button', { name: 'Date' }).click();
await page.locator('canvas').click({ position: { x: 200, y: 150 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 150 } });
// If second recipient is still a SIGNER (role change wasn't available),
// add a signature field for them to pass validation
if (!secondRecipientIsApprover) {
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({ position: { x: 200, y: 200 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 200 } });
}
// Complete the document
@@ -300,9 +283,7 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await expect(page.getByRole('link', { name: document.title })).toBeVisible();
});
test('should preserve field assignments when editing document with duplicates', async ({
page,
}) => {
test('should preserve field assignments when editing document with duplicates', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
@@ -333,23 +314,19 @@ test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
await page.getByText('Duplicate Recipient 1 (duplicate@example.com)').click();
// Verify their field is visible and can be selected
const firstRecipientFields = await page
.locator(`[data-field-type="SIGNATURE"]:not(:disabled)`)
.all();
const firstRecipientFields = await page.locator(`[data-field-type="SIGNATURE"]:not(:disabled)`).all();
expect(firstRecipientFields.length).toBeGreaterThan(0);
// Switch to second duplicate recipient
await page.getByText('Duplicate Recipient 2 (duplicate@example.com)').click();
// Verify they have their own field
const secondRecipientFields = await page
.locator(`[data-field-type="SIGNATURE"]:not(:disabled)`)
.all();
const secondRecipientFields = await page.locator(`[data-field-type="SIGNATURE"]:not(:disabled)`).all();
expect(secondRecipientFields.length).toBeGreaterThan(0);
// Add another field to the second duplicate
await page.getByRole('button', { name: 'Name' }).click();
await page.locator('canvas').click({ position: { x: 250, y: 150 } });
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 250, y: 150 } });
// Save changes
await page.getByRole('button', { name: 'Continue' }).click();
@@ -1,11 +1,6 @@
import { expect, test } from '@playwright/test';
import {
seedBlankDocument,
seedDraftDocument,
seedPendingDocument,
} from '@documenso/prisma/seed/documents';
import { seedBlankDocument, seedDraftDocument, seedPendingDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';
import { apiSignin } from '../fixtures/authentication';
@@ -1,7 +1,6 @@
import { expect, test } from '@playwright/test';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';
import { apiSignin } from '../fixtures/authentication';
@@ -1,20 +1,11 @@
import { expect, test } from '@playwright/test';
import {
DocumentSigningOrder,
DocumentStatus,
FieldType,
RecipientRole,
SigningStatus,
} from '@prisma/client';
import { DateTime } from 'luxon';
import path from 'node:path';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { prisma } from '@documenso/prisma';
import {
seedBlankDocument,
seedPendingDocumentWithFullFields,
} from '@documenso/prisma/seed/documents';
import { seedBlankDocument, seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';
import { DocumentSigningOrder, DocumentStatus, FieldType, RecipientRole, SigningStatus } from '@prisma/client';
import { DateTime } from 'luxon';
import { apiSignin } from '../fixtures/authentication';
import { signSignaturePad } from '../fixtures/signature';
@@ -92,7 +83,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) =>
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -100,7 +91,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) =>
});
await page.getByRole('button', { name: 'Email' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -120,9 +111,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) =>
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
});
test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipients', async ({
page,
}) => {
test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipients', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
@@ -158,7 +147,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -166,7 +155,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
});
await page.getByRole('button', { name: 'Email' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -177,7 +166,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
await page.getByText('User 2 (user2@example.com)').click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 500,
y: 100,
@@ -185,7 +174,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
});
await page.getByRole('button', { name: 'Email' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 500,
y: 200,
@@ -256,7 +245,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
await page.getByRole('option', { name: 'User 1 (user1@example.com)' }).click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100,
@@ -264,7 +253,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
});
await page.getByRole('button', { name: 'Email' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 200,
@@ -275,7 +264,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
await page.getByRole('option', { name: 'User 3 (user3@example.com)' }).click();
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 500,
y: 100,
@@ -283,7 +272,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
});
await page.getByRole('button', { name: 'Email' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 500,
y: 200,
@@ -303,9 +292,7 @@ test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipie
await expect(page.getByRole('link', { name: 'Test Title' })).toBeVisible();
});
test('[DOCUMENT_FLOW]: should not be able to create a document without signatures', async ({
page,
}) => {
test('[DOCUMENT_FLOW]: should not be able to create a document without signatures', async ({ page }) => {
const { user, team } = await seedUser();
const document = await seedBlankDocument(user, team.id);
@@ -336,9 +323,7 @@ test('[DOCUMENT_FLOW]: should not be able to create a document without signature
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Continue' }).click();
await expect(
page.getByRole('dialog').getByText('No signature field found').first(),
).toBeVisible();
await expect(page.getByRole('dialog').getByText('No signature field found').first()).toBeVisible();
});
test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) => {
@@ -381,12 +366,8 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) =
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
await page
.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Complete' : 'Approve' })
.click();
await page
.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Sign' : 'Approve' })
.click();
await page.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Complete' : 'Approve' }).click();
await page.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Sign' : 'Approve' }).click();
await page.waitForURL(`${signUrl}/complete`);
}
});
@@ -459,10 +440,7 @@ test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a
await page.getByRole('button', { name: 'Approve' }).click();
await expect(
page
.getByRole('dialog')
.getByText('You are about to complete approving the following document')
.first(),
page.getByRole('dialog').getByText('You are about to complete approving the following document').first(),
).toBeVisible();
await page.getByRole('button', { name: 'Approve' }).click();
@@ -576,7 +554,7 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
}
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({
position: {
x: 100,
y: 100 * i,
@@ -605,9 +583,7 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
expect(createdDocument?.recipients.length).toBe(3);
for (let i = 0; i < 3; i++) {
const recipient = createdDocument?.recipients.find(
(r) => r.email === `user${i + 1}@example.com`,
);
const recipient = createdDocument?.recipients.find((r) => r.email === `user${i + 1}@example.com`);
expect(recipient).not.toBeNull();
const fields = await prisma.field.findMany({
@@ -652,9 +628,7 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
expect(finalDocument?.status).toBe(DocumentStatus.COMPLETED);
});
test('[DOCUMENT_FLOW]: should prevent out-of-order signing in sequential mode', async ({
page,
}) => {
test('[DOCUMENT_FLOW]: should prevent out-of-order signing in sequential mode', async ({ page }) => {
const { user, team } = await seedUser();
const { document, recipients } = await seedPendingDocumentWithFullFields({