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,