mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
356 lines
13 KiB
TypeScript
356 lines
13 KiB
TypeScript
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 { apiSignin } from '../fixtures/authentication';
|
|
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 { page, team, document } = options;
|
|
|
|
// Step 1: Settings - Continue with defaults
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
|
|
|
// Step 2: Add duplicate recipients
|
|
await page.getByPlaceholder('Email').fill('duplicate@example.com');
|
|
await page.getByPlaceholder('Name').fill('Duplicate Recipient 1');
|
|
|
|
// Add second signer with same email
|
|
await page.getByRole('button', { name: 'Add Signer' }).click();
|
|
await page.getByLabel('Email').nth(1).fill('duplicate@example.com');
|
|
await page.getByLabel('Name').nth(1).fill('Duplicate Recipient 2');
|
|
|
|
// Add third signer with different email for comparison
|
|
await page.getByRole('button', { name: 'Add Signer' }).click();
|
|
await page.getByLabel('Email').nth(2).fill('unique@example.com');
|
|
await page.getByLabel('Name').nth(2).fill('Unique Recipient');
|
|
|
|
// Continue to fields
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
|
|
|
// 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.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.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 } });
|
|
|
|
// Continue to subject
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
|
|
|
|
// Step 4: Complete with subject and send
|
|
await page.waitForTimeout(2500);
|
|
await page.getByRole('button', { name: 'Send' }).click();
|
|
|
|
// Wait for send confirmation
|
|
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
|
|
|
await expect(page.getByRole('link', { name: document.title })).toBeVisible();
|
|
};
|
|
|
|
test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
|
|
test('should allow creating document with duplicate recipient emails', async ({ page }) => {
|
|
const { user, team } = await seedUser();
|
|
|
|
const document = await seedBlankDocument(user, team.id);
|
|
|
|
await apiSignin({
|
|
page,
|
|
email: user.email,
|
|
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
|
});
|
|
|
|
// Complete the flow
|
|
await completeDocumentFlowWithDuplicateRecipients({
|
|
page,
|
|
team,
|
|
document,
|
|
});
|
|
|
|
// Verify document was created successfully
|
|
await expect(page).toHaveURL(new RegExp(`/t/${team.url}/documents`));
|
|
});
|
|
|
|
test('should allow adding duplicate recipient after saving document initially', async ({
|
|
page,
|
|
}) => {
|
|
const { user, team } = await seedUser();
|
|
const document = await seedBlankDocument(user, team.id);
|
|
|
|
await apiSignin({
|
|
page,
|
|
email: user.email,
|
|
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
|
});
|
|
|
|
// Step 1: Settings - Continue with defaults
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
|
|
|
// Step 2: Add initial recipient
|
|
await page.getByPlaceholder('Email').fill('test@example.com');
|
|
await page.getByPlaceholder('Name').fill('Test Recipient');
|
|
|
|
// Continue to fields and add a field
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
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 } });
|
|
|
|
// Save the document by going to subject
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
|
|
|
|
// Navigate back to signers to add duplicate
|
|
await page.getByRole('button', { name: 'Go Back' }).click();
|
|
await page.getByRole('button', { name: 'Go Back' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
|
|
|
// Add duplicate recipient
|
|
await page.getByRole('button', { name: 'Add Signer' }).click();
|
|
await page.getByLabel('Email').nth(1).fill('test@example.com');
|
|
await page.getByLabel('Name').nth(1).fill('Test Recipient Duplicate');
|
|
|
|
// Continue and add field for duplicate
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
|
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Switch to duplicate recipient and add field
|
|
await page.getByRole('combobox').first().click();
|
|
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 } });
|
|
|
|
// Complete the flow
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
|
|
await page.waitForTimeout(2500);
|
|
await page.getByRole('button', { name: 'Send' }).click();
|
|
|
|
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
|
|
|
await expect(page.getByRole('link', { name: document.title })).toBeVisible();
|
|
});
|
|
|
|
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);
|
|
|
|
await apiSignin({
|
|
page,
|
|
email: user.email,
|
|
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
|
});
|
|
|
|
// Complete the document flow
|
|
await completeDocumentFlowWithDuplicateRecipients({
|
|
page,
|
|
team,
|
|
document,
|
|
});
|
|
|
|
// Navigate to documents list and get the document
|
|
await expect(page).toHaveURL(new RegExp(`/t/${team.url}/documents`));
|
|
|
|
const recipients = await prisma.recipient.findMany({
|
|
where: {
|
|
documentId: document.id,
|
|
},
|
|
});
|
|
|
|
expect(recipients).toHaveLength(3);
|
|
|
|
const tokens = recipients.map((r) => r.token);
|
|
|
|
expect(new Set(tokens).size).toBe(3); // All tokens should be unique
|
|
|
|
// Test each signing experience in separate browser contexts
|
|
for (const recipient of recipients) {
|
|
// Navigate to signing URL
|
|
await page.goto(`/sign/${recipient.token}`, {
|
|
waitUntil: 'networkidle',
|
|
});
|
|
|
|
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);
|
|
|
|
// Verify recipient name is correct
|
|
await expect(page.getByLabel('Full Name')).toHaveValue(recipient.name);
|
|
|
|
// Sign the document
|
|
await signSignaturePad(page);
|
|
|
|
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();
|
|
|
|
// Verify completion
|
|
await page.waitForURL(`/sign/${recipient?.token}/complete`);
|
|
await expect(page.getByText('Document Signed')).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should handle duplicate recipient workflow with different field types', async ({
|
|
page,
|
|
}) => {
|
|
const { user, team } = await seedUser();
|
|
const document = await seedBlankDocument(user, team.id);
|
|
|
|
await apiSignin({
|
|
page,
|
|
email: user.email,
|
|
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
|
});
|
|
|
|
// Step 1: Settings
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
|
|
// Step 2: Add duplicate recipients with different roles
|
|
await page.getByPlaceholder('Email').fill('signer@example.com');
|
|
await page.getByPlaceholder('Name').fill('Signer Role');
|
|
|
|
await page.getByRole('button', { name: 'Add Signer' }).click();
|
|
await page.getByLabel('Email').nth(1).fill('signer@example.com');
|
|
await page.getByLabel('Name').nth(1).fill('Approver Role');
|
|
|
|
// Change second recipient role if role selector is available
|
|
const roleDropdown = page.getByLabel('Role').nth(1);
|
|
|
|
if (await roleDropdown.isVisible()) {
|
|
await roleDropdown.click();
|
|
await page.getByText('Approver').click();
|
|
}
|
|
|
|
// Step 3: Add different field types for each duplicate
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
|
|
// Add signature for first recipient
|
|
await page.getByRole('button', { name: 'Signature' }).click();
|
|
await page.locator('canvas').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 } });
|
|
|
|
// Add date field for second recipient
|
|
await page.getByRole('button', { name: 'Date' }).click();
|
|
await page.locator('canvas').click({ position: { x: 200, y: 150 } });
|
|
|
|
// Complete the document
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
|
|
await page.getByRole('button', { name: 'Send' }).click();
|
|
|
|
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
|
|
|
await expect(page.getByRole('link', { name: document.title })).toBeVisible();
|
|
});
|
|
|
|
test('should preserve field assignments when editing document with duplicates', async ({
|
|
page,
|
|
}) => {
|
|
const { user, team } = await seedUser();
|
|
const document = await seedBlankDocument(user, team.id);
|
|
|
|
await apiSignin({
|
|
page,
|
|
email: user.email,
|
|
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
|
});
|
|
|
|
// Create document with duplicates and fields
|
|
await completeDocumentFlowWithDuplicateRecipients({
|
|
page,
|
|
team,
|
|
document,
|
|
});
|
|
|
|
// Navigate back to edit the document
|
|
await page.goto(`/t/${team.url}/documents/${document.id}/edit`);
|
|
|
|
// Go to fields step
|
|
await page.getByRole('button', { name: 'Continue' }).click(); // Settings
|
|
await page.getByRole('button', { name: 'Continue' }).click(); // Signers
|
|
|
|
// Verify fields are assigned to correct recipients
|
|
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
|
|
|
|
// Click on first duplicate recipient
|
|
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();
|
|
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();
|
|
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 } });
|
|
|
|
// Save changes
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
|
|
await page.waitForTimeout(2500);
|
|
await page.getByRole('button', { name: 'Send' }).click();
|
|
|
|
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
|
|
|
|
await expect(page.getByRole('link', { name: document.title })).toBeVisible();
|
|
});
|
|
});
|