Files
sign/packages/app-tests/e2e/document-flow/stepper-component.spec.ts

669 lines
20 KiB
TypeScript
Raw Normal View History

2023-12-07 15:08:16 +11:00
import { expect, test } from '@playwright/test';
import {
DocumentSigningOrder,
DocumentStatus,
FieldType,
RecipientRole,
SigningStatus,
2025-03-03 14:49:28 +11:00
} from '@prisma/client';
import { DateTime } from 'luxon';
import path from 'node:path';
import { getRecipientByEmail } from '@documenso/lib/server-only/recipient/get-recipient-by-email';
import { prisma } from '@documenso/prisma';
2024-05-27 04:17:03 +00:00
import {
seedBlankDocument,
seedPendingDocumentWithFullFields,
} from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
2024-03-28 13:13:29 +08:00
import { apiSignin } from '../fixtures/authentication';
2025-01-02 15:33:37 +11:00
import { signSignaturePad } from '../fixtures/signature';
2023-12-07 15:08:16 +11:00
// Can't use the function in server-only/document due to it indirectly using
// require imports.
const getDocumentByToken = async (token: string) => {
return await prisma.document.findFirstOrThrow({
where: {
2025-01-13 13:41:53 +11:00
recipients: {
some: {
token,
},
},
},
});
};
test('[DOCUMENT_FLOW]: should be able to upload a PDF document', async ({ page }) => {
2024-03-28 13:13:29 +08:00
const user = await seedUser();
await apiSignin({
page,
email: user.email,
});
2023-12-07 15:08:16 +11:00
// Upload document.
2023-12-07 15:08:16 +11:00
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.locator('input[type=file]').evaluate((e) => {
if (e instanceof HTMLInputElement) {
e.click();
}
}),
]);
await fileChooser.setFiles(path.join(__dirname, '../../../../assets/example.pdf'));
2023-12-07 15:08:16 +11:00
// Wait to be redirected to the edit page.
2023-12-07 15:08:16 +11:00
await page.waitForURL(/\/documents\/\d+/);
});
test('[DOCUMENT_FLOW]: should be able to create a document', async ({ page }) => {
const user = await seedUser();
const document = await seedBlankDocument(user);
await apiSignin({
page,
email: user.email,
redirectPath: `/documents/${document.id}/edit`,
});
const documentTitle = `example-${Date.now()}.pdf`;
2023-12-07 15:08:16 +11:00
2024-03-28 13:13:29 +08:00
// Set general settings
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
2023-12-07 15:08:16 +11:00
await page.getByLabel('Title').fill(documentTitle);
await page.getByRole('button', { name: 'Continue' }).click();
// Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
await page.getByLabel('Email*').fill('user1@example.com');
await page.getByLabel('Name').fill('User 1');
await page.getByRole('button', { name: 'Continue' }).click();
// Add fields
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Signature' }).click();
2023-12-07 15:08:16 +11:00
await page.locator('canvas').click({
position: {
x: 100,
y: 100,
},
});
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Email' }).click();
2023-12-07 15:08:16 +11:00
await page.locator('canvas').click({
position: {
x: 100,
y: 200,
},
});
await page.getByRole('button', { name: 'Continue' }).click();
// Add subject and send
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
await page.waitForTimeout(2500);
2023-12-07 15:08:16 +11:00
await page.getByRole('button', { name: 'Send' }).click();
await page.waitForURL('/documents');
// Assert document was created
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
});
2024-03-08 14:54:18 +00:00
test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipients', async ({
page,
}) => {
2024-03-28 13:13:29 +08:00
const user = await seedUser();
const document = await seedBlankDocument(user);
2024-03-28 13:13:29 +08:00
await apiSignin({
page,
email: user.email,
redirectPath: `/documents/${document.id}/edit`,
2024-03-28 13:13:29 +08:00
});
2024-03-08 14:54:18 +00:00
const documentTitle = `example-${Date.now()}.pdf`;
2024-03-08 14:54:18 +00:00
// Set title
2024-03-28 13:13:29 +08:00
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
2024-03-08 14:54:18 +00:00
await page.getByLabel('Title').fill(documentTitle);
await page.getByRole('button', { name: 'Continue' }).click();
// Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
2024-03-28 13:13:29 +08:00
// Add 2 signers.
await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByPlaceholder('Name').fill('User 1');
2024-03-08 14:54:18 +00:00
await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByLabel('Email').nth(1).fill('user2@example.com');
await page.getByLabel('Name').nth(1).fill('User 2');
2024-03-08 14:54:18 +00:00
await page.getByRole('button', { name: 'Continue' }).click();
// Add fields
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Signature' }).click();
2024-03-08 14:54:18 +00:00
await page.locator('canvas').click({
position: {
x: 100,
y: 100,
},
});
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Email' }).click();
2024-03-08 14:54:18 +00:00
await page.locator('canvas').click({
position: {
x: 100,
y: 200,
},
});
await page.getByText('User 1 (user1@example.com)').click();
await page.getByText('User 2 (user2@example.com)').click();
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Signature' }).click();
2024-03-08 14:54:18 +00:00
await page.locator('canvas').click({
position: {
x: 500,
y: 100,
},
});
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Email' }).click();
2024-03-08 14:54:18 +00:00
await page.locator('canvas').click({
position: {
x: 500,
y: 200,
},
});
await page.getByRole('button', { name: 'Continue' }).click();
// Add subject and send
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
await page.waitForTimeout(2500);
2024-03-08 14:54:18 +00:00
await page.getByRole('button', { name: 'Send' }).click();
await page.waitForURL('/documents');
// Assert document was created
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
});
2024-03-21 16:15:29 +00:00
2024-05-27 04:17:03 +00:00
test('[DOCUMENT_FLOW]: should be able to create a document with multiple recipients with different roles', async ({
page,
}) => {
const user = await seedUser();
const document = await seedBlankDocument(user);
await apiSignin({
page,
email: user.email,
redirectPath: `/documents/${document.id}/edit`,
});
// Set title
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByLabel('Title').fill('Test Title');
await page.getByRole('button', { name: 'Continue' }).click();
// Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
// Add 2 signers.
await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByPlaceholder('Name').fill('User 1');
await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByLabel('Email').nth(1).fill('user2@example.com');
await page.getByLabel('Name').nth(1).fill('User 2');
2024-05-27 04:17:03 +00:00
await page.locator('button[role="combobox"]').nth(1).click();
await page.getByLabel('Receives copy').click();
await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByLabel('Email').nth(2).fill('user3@example.com');
await page.getByLabel('Name').nth(2).fill('User 3');
2024-05-27 04:17:03 +00:00
await page.locator('button[role="combobox"]').nth(2).click();
await page.getByLabel('Needs to approve').click();
await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByLabel('Email').nth(3).fill('user4@example.com');
await page.getByLabel('Name').nth(3).fill('User 4');
2024-05-27 04:17:03 +00:00
await page.locator('button[role="combobox"]').nth(3).click();
await page.getByLabel('Needs to view').click();
await page.getByRole('button', { name: 'Continue' }).click();
// Add fields
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
2024-07-19 11:32:49 +10:00
await page.locator('button[role="combobox"]').nth(0).click();
await page.getByTitle('User 1 (user1@example.com)').click();
await page.getByRole('button', { name: 'Signature' }).click();
2024-05-27 04:17:03 +00:00
await page.locator('canvas').click({
position: {
x: 100,
y: 100,
},
});
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Email' }).click();
2024-05-27 04:17:03 +00:00
await page.locator('canvas').click({
position: {
x: 100,
y: 200,
},
});
2024-07-19 11:32:49 +10:00
await page.locator('button[role="combobox"]').nth(0).click();
await page.getByTitle('User 3 (user3@example.com)').click();
2024-05-27 04:17:03 +00:00
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Signature' }).click();
2024-05-27 04:17:03 +00:00
await page.locator('canvas').click({
position: {
x: 500,
y: 100,
},
});
2024-07-19 11:32:49 +10:00
await page.getByRole('button', { name: 'Email' }).click();
2024-05-27 04:17:03 +00:00
await page.locator('canvas').click({
position: {
x: 500,
y: 200,
},
});
await page.getByRole('button', { name: 'Continue' }).click();
// Add subject and send
await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
await page.waitForTimeout(2500);
2024-05-27 04:17:03 +00:00
await page.getByRole('button', { name: 'Send' }).click();
await page.waitForURL('/documents');
// Assert document was created
await expect(page.getByRole('link', { name: 'Test Title' })).toBeVisible();
});
feat: force signature fields for document signers (#1139) ## Description Show a dialog when the document has signers with no signature fields placed. ## Changes Made Created a new dialog that'll be triggered when the document owner tries to send a document to the signers without placing signature fields. The document owners can't proceed to the next step unless they add signature fields. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. https://github.com/documenso/documenso/assets/25515812/f1b5c34e-2ce0-40e3-804c-f05d23045710 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced "Direct Links" for async signing, allowing users to create documents from templates using public links. - Added `MissingSignatureFieldDialog` component to ensure users don't miss adding signature fields. - **Enhancements** - Updated blog content to provide guidance on contract management and announce new pricing plans. - **Bug Fixes** - Improved async signing process for better efficiency and control. - **Refactor** - Improved internal code structure and import order for stripe-related functionality. - **Tests** - Enhanced e2e tests to verify signature presence before document creation and updated test flows for document approval. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 11:01:57 +03:00
test('[DOCUMENT_FLOW]: should not be able to create a document without signatures', async ({
page,
}) => {
2024-03-28 13:13:29 +08:00
const user = await seedUser();
const document = await seedBlankDocument(user);
2024-03-28 13:13:29 +08:00
await apiSignin({
page,
email: user.email,
redirectPath: `/documents/${document.id}/edit`,
2024-03-28 13:13:29 +08:00
});
2024-03-21 16:15:29 +00:00
const documentTitle = `example-${Date.now()}.pdf`;
2024-03-21 16:15:29 +00:00
// Set title
2024-03-28 13:13:29 +08:00
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
2024-03-21 16:15:29 +00:00
await page.getByLabel('Title').fill(documentTitle);
await page.getByRole('button', { name: 'Continue' }).click();
// Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
2024-03-28 13:13:29 +08:00
await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByPlaceholder('Name').fill('User 1');
2024-03-21 16:15:29 +00:00
await page.getByRole('button', { name: 'Continue' }).click();
// Add fields
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Continue' }).click();
feat: force signature fields for document signers (#1139) ## Description Show a dialog when the document has signers with no signature fields placed. ## Changes Made Created a new dialog that'll be triggered when the document owner tries to send a document to the signers without placing signature fields. The document owners can't proceed to the next step unless they add signature fields. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. https://github.com/documenso/documenso/assets/25515812/f1b5c34e-2ce0-40e3-804c-f05d23045710 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced "Direct Links" for async signing, allowing users to create documents from templates using public links. - Added `MissingSignatureFieldDialog` component to ensure users don't miss adding signature fields. - **Enhancements** - Updated blog content to provide guidance on contract management and announce new pricing plans. - **Bug Fixes** - Improved async signing process for better efficiency and control. - **Refactor** - Improved internal code structure and import order for stripe-related functionality. - **Tests** - Enhanced e2e tests to verify signature presence before document creation and updated test flows for document approval. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 11:01:57 +03:00
await expect(
page.getByRole('dialog').getByText('No signature field found').first(),
).toBeVisible();
2024-03-21 16:15:29 +00:00
});
2024-03-21 16:28:42 +00:00
2024-05-27 04:17:03 +00:00
test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) => {
const user = await seedUser();
const { recipients } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['user@documenso.com', 'approver@documenso.com'],
recipientsCreateOptions: [
{
email: 'user@documenso.com',
role: RecipientRole.SIGNER,
},
{
email: 'approver@documenso.com',
role: RecipientRole.APPROVER,
},
],
fields: [FieldType.SIGNATURE],
});
for (const recipient of recipients) {
2025-01-13 13:41:53 +11:00
const { token, fields, role } = recipient;
2024-05-27 04:17:03 +00:00
const signUrl = `/sign/${token}`;
await page.goto(signUrl);
await expect(
page.getByRole('heading', {
name: role === RecipientRole.SIGNER ? 'Sign Document' : 'Approve Document',
}),
).toBeVisible();
2025-01-02 15:33:37 +11:00
await signSignaturePad(page);
2024-05-27 04:17:03 +00:00
2025-01-13 13:41:53 +11:00
for (const field of fields) {
2024-05-27 04:17:03 +00:00
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
await page.getByRole('button', { name: 'Complete' }).click();
await page
.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Sign' : 'Approve' })
.click();
await page.waitForURL(`${signUrl}/complete`);
}
});
test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a document and redirect to redirect url', async ({
2024-03-21 16:28:42 +00:00
page,
}) => {
2024-03-28 13:13:29 +08:00
const user = await seedUser();
const document = await seedBlankDocument(user);
2024-03-28 13:13:29 +08:00
await apiSignin({
page,
email: user.email,
redirectPath: `/documents/${document.id}/edit`,
2024-03-28 13:13:29 +08:00
});
2024-03-21 16:28:42 +00:00
const documentTitle = `example-${Date.now()}.pdf`;
2024-03-21 16:28:42 +00:00
2024-03-28 13:13:29 +08:00
// Set title & advanced redirect
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
2024-03-21 16:28:42 +00:00
await page.getByLabel('Title').fill(documentTitle);
2024-03-28 13:13:29 +08:00
await page.getByRole('button', { name: 'Advanced Options' }).click();
await page.getByLabel('Redirect URL').fill('https://documenso.com');
2024-03-21 16:28:42 +00:00
await page.getByRole('button', { name: 'Continue' }).click();
// Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
2024-03-28 13:13:29 +08:00
await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByPlaceholder('Name').fill('User 1');
2024-03-21 16:28:42 +00:00
feat: force signature fields for document signers (#1139) ## Description Show a dialog when the document has signers with no signature fields placed. ## Changes Made Created a new dialog that'll be triggered when the document owner tries to send a document to the signers without placing signature fields. The document owners can't proceed to the next step unless they add signature fields. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. https://github.com/documenso/documenso/assets/25515812/f1b5c34e-2ce0-40e3-804c-f05d23045710 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced "Direct Links" for async signing, allowing users to create documents from templates using public links. - Added `MissingSignatureFieldDialog` component to ensure users don't miss adding signature fields. - **Enhancements** - Updated blog content to provide guidance on contract management and announce new pricing plans. - **Bug Fixes** - Improved async signing process for better efficiency and control. - **Refactor** - Improved internal code structure and import order for stripe-related functionality. - **Tests** - Enhanced e2e tests to verify signature presence before document creation and updated test flows for document approval. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 11:01:57 +03:00
await page.getByRole('combobox').click();
await page.getByLabel('Needs to approve').getByText('Needs to approve').click();
2024-03-21 16:28:42 +00:00
await page.getByRole('button', { name: 'Continue' }).click();
// Add fields
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Continue' }).click();
await page.waitForTimeout(2500);
2024-03-21 16:28:42 +00:00
await page.getByRole('button', { name: 'Send' }).click();
await page.waitForURL('/documents');
// Assert document was created
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
await page.getByRole('link', { name: documentTitle }).click();
2024-03-28 13:13:29 +08:00
await page.waitForURL(/\/documents\/\d+/);
2024-03-21 16:28:42 +00:00
2024-03-28 13:13:29 +08:00
const url = page.url().split('/');
2024-03-21 16:28:42 +00:00
const documentId = url[url.length - 1];
const { token } = await getRecipientByEmail({
email: 'user1@example.com',
documentId: Number(documentId),
});
await page.goto(`/sign/${token}`);
await page.waitForURL(`/sign/${token}`);
// Check if document has been viewed
const { status } = await getDocumentByToken(token);
2024-03-21 16:28:42 +00:00
expect(status).toBe(DocumentStatus.PENDING);
await page.getByRole('button', { name: 'Complete' }).click();
feat: force signature fields for document signers (#1139) ## Description Show a dialog when the document has signers with no signature fields placed. ## Changes Made Created a new dialog that'll be triggered when the document owner tries to send a document to the signers without placing signature fields. The document owners can't proceed to the next step unless they add signature fields. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. https://github.com/documenso/documenso/assets/25515812/f1b5c34e-2ce0-40e3-804c-f05d23045710 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced "Direct Links" for async signing, allowing users to create documents from templates using public links. - Added `MissingSignatureFieldDialog` component to ensure users don't miss adding signature fields. - **Enhancements** - Updated blog content to provide guidance on contract management and announce new pricing plans. - **Bug Fixes** - Improved async signing process for better efficiency and control. - **Refactor** - Improved internal code structure and import order for stripe-related functionality. - **Tests** - Enhanced e2e tests to verify signature presence before document creation and updated test flows for document approval. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 11:01:57 +03:00
await expect(page.getByRole('dialog').getByText('Complete Approval').first()).toBeVisible();
await page.getByRole('button', { name: 'Approve' }).click();
2024-03-21 16:28:42 +00:00
await page.waitForURL('https://documenso.com');
await expect(async () => {
// Check if document has been signed
const { status: completedStatus } = await getDocumentByToken(token);
expect(completedStatus).toBe(DocumentStatus.COMPLETED);
}).toPass();
2024-03-21 16:28:42 +00:00
});
2024-05-27 04:17:03 +00:00
test('[DOCUMENT_FLOW]: should be able to sign a document with custom date', async ({ page }) => {
const user = await seedUser();
const now = DateTime.utc();
2024-05-27 04:17:03 +00:00
const { document, recipients } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['user1@example.com'],
fields: [FieldType.DATE],
});
2025-01-13 13:41:53 +11:00
const { token, fields } = recipients[0];
const [recipientField] = fields;
2024-05-27 04:17:03 +00:00
await page.goto(`/sign/${token}`);
await page.waitForURL(`/sign/${token}`);
await page.locator(`#field-${recipientField.id}`).getByRole('button').click();
await page.getByRole('button', { name: 'Complete' }).click();
await expect(page.getByRole('dialog').getByText('Complete Signing').first()).toBeVisible();
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`/sign/${token}/complete`);
await expect(page.getByText('Document Signed')).toBeVisible();
const field = await prisma.field.findFirst({
where: {
2025-01-13 13:41:53 +11:00
recipient: {
2024-05-27 04:17:03 +00:00
email: 'user1@example.com',
},
documentId: Number(document.id),
},
});
const insertedDate = DateTime.fromFormat(field?.customText ?? '', 'yyyy-MM-dd hh:mm a');
expect(Math.abs(insertedDate.diff(now).minutes)).toBeLessThanOrEqual(1);
await expect(async () => {
// Check if document has been signed
const { status: completedStatus } = await getDocumentByToken(token);
2024-05-27 04:17:03 +00:00
expect(completedStatus).toBe(DocumentStatus.COMPLETED);
}).toPass();
2024-05-27 04:17:03 +00:00
});
test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recipients in sequential order', async ({
page,
}) => {
const user = await seedUser();
const document = await seedBlankDocument(user);
await apiSignin({
page,
email: user.email,
redirectPath: `/documents/${document.id}/edit`,
});
const documentTitle = `Sequential-Signing-${Date.now()}.pdf`;
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByLabel('Title').fill(documentTitle);
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
await page.getByLabel('Enable signing order').check();
for (let i = 1; i <= 3; i++) {
if (i > 1) {
await page.getByRole('button', { name: 'Add Signer' }).click();
}
await page
.getByLabel('Email')
.nth(i - 1)
.focus();
await page
.getByLabel('Email')
.nth(i - 1)
.fill(`user${i}@example.com`);
await page
.getByLabel('Name')
.nth(i - 1)
.fill(`User ${i}`);
}
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
for (let i = 1; i <= 3; i++) {
if (i > 1) {
await page.getByText(`User ${i} (user${i}@example.com)`).click();
}
await page.getByRole('button', { name: 'Signature' }).click();
await page.locator('canvas').click({
position: {
x: 100,
y: 100 * i,
},
});
await page.getByText(`User ${i} (user${i}@example.com)`).click();
}
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('/documents');
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
const createdDocument = await prisma.document.findFirst({
where: { title: documentTitle },
2025-01-13 13:41:53 +11:00
include: { recipients: true },
});
expect(createdDocument).not.toBeNull();
2025-01-13 13:41:53 +11:00
expect(createdDocument?.recipients.length).toBe(3);
for (let i = 0; i < 3; i++) {
2025-01-13 13:41:53 +11:00
const recipient = createdDocument?.recipients.find(
(r) => r.email === `user${i + 1}@example.com`,
);
expect(recipient).not.toBeNull();
const fields = await prisma.field.findMany({
where: { recipientId: recipient?.id, documentId: createdDocument?.id },
});
const recipientField = fields[0];
if (i > 0) {
const previousRecipient = await prisma.recipient.findFirst({
where: { email: `user${i}@example.com`, documentId: createdDocument?.id },
});
expect(previousRecipient?.signingStatus).toBe(SigningStatus.SIGNED);
}
await page.goto(`/sign/${recipient?.token}`);
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
2025-01-02 15:33:37 +11:00
await signSignaturePad(page);
await page.locator(`#field-${recipientField.id}`).getByRole('button').click();
await page.getByRole('button', { name: 'Complete' }).click();
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`/sign/${recipient?.token}/complete`);
await expect(page.getByText('Document Signed')).toBeVisible();
const updatedRecipient = await prisma.recipient.findFirst({
where: { id: recipient?.id },
});
expect(updatedRecipient?.signingStatus).toBe(SigningStatus.SIGNED);
}
// Wait for the document to be signed.
await page.waitForTimeout(5000);
const finalDocument = await prisma.document.findFirst({
where: { id: createdDocument?.id },
});
expect(finalDocument?.status).toBe(DocumentStatus.COMPLETED);
});
test('[DOCUMENT_FLOW]: should prevent out-of-order signing in sequential mode', async ({
page,
}) => {
const user = await seedUser();
const { document, recipients } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
fields: [FieldType.SIGNATURE],
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }, { signingOrder: 3 }],
updateDocumentOptions: {
documentMeta: {
create: {
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
},
},
});
const pendingRecipient = recipients.find((r) => r.signingOrder === 2);
await page.goto(`/sign/${pendingRecipient?.token}`);
await expect(page).toHaveURL(`/sign/${pendingRecipient?.token}/waiting`);
const activeRecipient = recipients.find((r) => r.signingOrder === 1);
await page.goto(`/sign/${activeRecipient?.token}`);
await expect(page).not.toHaveURL(`/sign/${activeRecipient?.token}/waiting`);
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
});