From 9b5346efef1f7cc631f57502a065d3d3951a368d Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 8 Mar 2024 14:54:18 +0000 Subject: [PATCH 1/4] chore: add test for multiple recipient --- .../e2e/pr-718-add-stepper-component.spec.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts b/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts index 6e03979c0..e482c4172 100644 --- a/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts +++ b/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts @@ -73,3 +73,98 @@ test(`[PR-718]: should be able to create a document`, async ({ page }) => { // Assert document was created await expect(page.getByRole('link', { name: documentTitle })).toBeVisible(); }); + +test('should be able to create a document with multiple recipients', async ({ page }) => { + await page.goto('/signin'); + + const documentTitle = `example-${Date.now()}.pdf`; + + // Sign in + await page.getByLabel('Email').fill(TEST_USER.email); + await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); + await page.getByRole('button', { name: 'Sign In' }).click(); + + // Upload document + 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')); + + // Wait to be redirected to the edit page + await page.waitForURL(/\/documents\/\d+/); + + // Set title + await expect(page.getByRole('heading', { name: 'Add Title' })).toBeVisible(); + + 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: 'Add Signer' }).click(); + + await page.getByLabel('Email*').nth(1).fill('user2@example.com'); + await page.getByLabel('Name').nth(1).fill('User 2'); + + await page.getByRole('button', { name: 'Continue' }).click(); + + // Add fields + await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible(); + + await page.getByRole('button', { name: 'User 1 Signature' }).click(); + await page.locator('canvas').click({ + position: { + x: 100, + y: 100, + }, + }); + + await page.getByRole('button', { name: 'Email Email' }).click(); + 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(); + + await page.getByRole('button', { name: 'User 2 Signature' }).click(); + await page.locator('canvas').click({ + position: { + x: 500, + y: 100, + }, + }); + + await page.getByRole('button', { name: 'Email Email' }).click(); + 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: 'Add Subject' })).toBeVisible(); + await page.getByRole('button', { name: 'Send' }).click(); + + await page.waitForURL('/documents'); + + // Assert document was created + await expect(page.getByRole('link', { name: documentTitle })).toBeVisible(); +}); From 1cd7dd236b517134385444cce4c8cfe260d5b928 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Thu, 21 Mar 2024 16:15:29 +0000 Subject: [PATCH 2/4] chore: test signing a document --- .../src/app/(marketing)/open/bar-metrics.tsx | 2 +- .../e2e/pr-718-add-stepper-component.spec.ts | 84 +++++++++++++++++++ packages/app-tests/package.json | 1 + packages/app-tests/playwright.config.ts | 8 +- .../document/get-document-by-token.ts | 27 ++++-- 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx b/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx index 940adb8fc..8a01c5ecc 100644 --- a/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx +++ b/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx @@ -1,6 +1,6 @@ 'use client'; -import { HTMLAttributes } from 'react'; +import type { HTMLAttributes } from 'react'; import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; diff --git a/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts b/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts index e482c4172..3318afe7a 100644 --- a/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts +++ b/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts @@ -1,6 +1,9 @@ import { expect, test } from '@playwright/test'; import path from 'node:path'; +import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token'; +import { getRecipientByEmail } from '@documenso/lib/server-only/recipient/get-recipient-by-email'; +import { DocumentStatus } from '@documenso/prisma/client'; import { TEST_USER } from '@documenso/prisma/seed/pr-718-add-stepper-component'; test(`[PR-718]: should be able to create a document`, async ({ page }) => { @@ -168,3 +171,84 @@ test('should be able to create a document with multiple recipients', async ({ pa // Assert document was created await expect(page.getByRole('link', { name: documentTitle })).toBeVisible(); }); + +test('should be able to create, send and sign a document', async ({ page }) => { + await page.goto('/signin'); + + const documentTitle = `example-${Date.now()}.pdf`; + + // Sign in + await page.getByLabel('Email').fill(TEST_USER.email); + await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); + await page.getByRole('button', { name: 'Sign In' }).click(); + + // Upload document + 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')); + + // Wait to be redirected to the edit page + await page.waitForURL(/\/documents\/\d+/); + + // Set title + await expect(page.getByRole('heading', { name: 'Add Title' })).toBeVisible(); + + 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(); + await page.getByRole('button', { name: 'Continue' }).click(); + + // Add subject and send + await expect(page.getByRole('heading', { name: 'Add Subject' })).toBeVisible(); + 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(); + + const url = await page.url().split('/'); + 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 }); + expect(status).toBe(DocumentStatus.PENDING); + + await page.getByRole('button', { name: 'Complete' }).click(); + await expect(page.getByRole('dialog').getByText('Sign Document')).toBeVisible(); + await page.getByRole('button', { name: 'Sign' }).click(); + + await page.waitForURL(`/sign/${token}/complete`); + await expect(page.getByText('You have signed')).toBeVisible(); + + // Check if document has been signed + const { status: completedStatus } = await getDocumentByToken({ token }); + expect(completedStatus).toBe(DocumentStatus.COMPLETED); +}); diff --git a/packages/app-tests/package.json b/packages/app-tests/package.json index 9dcb32f7d..84f14d469 100644 --- a/packages/app-tests/package.json +++ b/packages/app-tests/package.json @@ -6,6 +6,7 @@ "main": "index.js", "scripts": { "test:dev": "playwright test", + "test-ui:dev": "playwright test --ui", "test:e2e": "start-server-and-test \"npm run start -w @documenso/web\" http://localhost:3000 \"playwright test\"" }, "keywords": [], diff --git a/packages/app-tests/playwright.config.ts b/packages/app-tests/playwright.config.ts index 672c2f7ef..65ba20455 100644 --- a/packages/app-tests/playwright.config.ts +++ b/packages/app-tests/playwright.config.ts @@ -29,7 +29,13 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', - video: 'retain-on-failure', + // BEFORE MERGE: video: 'retain-on-failure', + video: 'on', + + // REMOVE BEFORE MERGE + launchOptions: { + slowMo: 500, + }, }, timeout: 30_000, diff --git a/packages/lib/server-only/document/get-document-by-token.ts b/packages/lib/server-only/document/get-document-by-token.ts index d242e72fd..1594efbe4 100644 --- a/packages/lib/server-only/document/get-document-by-token.ts +++ b/packages/lib/server-only/document/get-document-by-token.ts @@ -1,13 +1,30 @@ import { prisma } from '@documenso/prisma'; import type { DocumentWithRecipient } from '@documenso/prisma/types/document-with-recipient'; -export interface GetDocumentAndSenderByTokenOptions { +export type GetDocumentByTokenOptions = { token: string; -} +}; -export interface GetDocumentAndRecipientByTokenOptions { - token: string; -} +export type GetDocumentAndSenderByTokenOptions = GetDocumentByTokenOptions; +export type GetDocumentAndRecipientByTokenOptions = GetDocumentByTokenOptions; + +export const getDocumentByToken = async ({ token }: GetDocumentByTokenOptions) => { + if (!token) { + throw new Error('Missing token'); + } + + const result = await prisma.document.findFirstOrThrow({ + where: { + Recipient: { + some: { + token, + }, + }, + }, + }); + + return result; +}; export const getDocumentAndSenderByToken = async ({ token, From 5377d27c6a2dfda807369e930f75c4f4596d3da5 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Thu, 21 Mar 2024 16:28:42 +0000 Subject: [PATCH 3/4] chore: test for redirect url --- .../e2e/pr-718-add-stepper-component.spec.ts | 85 +++++++++++++++++++ .../primitives/document-flow/add-subject.tsx | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts b/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts index 3318afe7a..4327935bb 100644 --- a/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts +++ b/packages/app-tests/e2e/pr-718-add-stepper-component.spec.ts @@ -252,3 +252,88 @@ test('should be able to create, send and sign a document', async ({ page }) => { const { status: completedStatus } = await getDocumentByToken({ token }); expect(completedStatus).toBe(DocumentStatus.COMPLETED); }); + +test('should be able to create, send with redirect url, sign a document and redirect to redirect url', async ({ + page, +}) => { + await page.goto('/signin'); + + const documentTitle = `example-${Date.now()}.pdf`; + + // Sign in + await page.getByLabel('Email').fill(TEST_USER.email); + await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); + await page.getByRole('button', { name: 'Sign In' }).click(); + + // Upload document + 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')); + + // Wait to be redirected to the edit page + await page.waitForURL(/\/documents\/\d+/); + + // Set title + await expect(page.getByRole('heading', { name: 'Add Title' })).toBeVisible(); + + 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(); + await page.getByRole('button', { name: 'Continue' }).click(); + + // Add subject and send + await expect(page.getByRole('heading', { name: 'Add Subject' })).toBeVisible(); + await page.getByRole('button', { name: 'Advanced Options' }).click(); + await page.getByLabel('Redirect URL').fill('https://documenso.com'); + + 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(); + + const url = await page.url().split('/'); + 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 }); + expect(status).toBe(DocumentStatus.PENDING); + + await page.getByRole('button', { name: 'Complete' }).click(); + await expect(page.getByRole('dialog').getByText('Sign Document')).toBeVisible(); + await page.getByRole('button', { name: 'Sign' }).click(); + + await page.waitForURL('https://documenso.com'); + + // Check if document has been signed + const { status: completedStatus } = await getDocumentByToken({ token }); + expect(completedStatus).toBe(DocumentStatus.COMPLETED); +}); diff --git a/packages/ui/primitives/document-flow/add-subject.tsx b/packages/ui/primitives/document-flow/add-subject.tsx index bfc7f3fc5..aa0bc148f 100644 --- a/packages/ui/primitives/document-flow/add-subject.tsx +++ b/packages/ui/primitives/document-flow/add-subject.tsx @@ -230,7 +230,7 @@ export const AddSubjectFormPartial = ({