feat: add envelope editor

This commit is contained in:
David Nguyen
2025-10-12 23:35:54 +11:00
parent bf89bc781b
commit 0da8e7dbc6
307 changed files with 24657 additions and 3681 deletions

View File

@ -4,10 +4,6 @@ import fs from 'fs';
import path from 'path';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import {
mapDocumentIdToSecondaryId,
mapSecondaryIdToTemplateId,
} from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
@ -33,7 +29,7 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
await apiSignin({
page,
email: user.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title.
@ -79,13 +75,13 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the correct values.
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));
const documentId = Number(page.url().split('/').pop());
const documentId = page.url().split('/').pop();
const document = await prisma.envelope.findFirstOrThrow({
where: {
secondaryId: mapDocumentIdToSecondaryId(documentId),
id: documentId,
},
include: {
recipients: true,
@ -136,7 +132,7 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
await apiSignin({
page,
email: owner.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title.
@ -182,13 +178,13 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the correct values.
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));
const documentId = Number(page.url().split('/').pop());
const documentId = page.url().split('/').pop();
const document = await prisma.envelope.findFirstOrThrow({
where: {
secondaryId: mapDocumentIdToSecondaryId(documentId),
id: documentId,
},
include: {
recipients: true,
@ -244,7 +240,7 @@ test('[TEMPLATE]: should create a document from a template with custom document'
await apiSignin({
page,
email: user.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title
@ -288,13 +284,13 @@ test('[TEMPLATE]: should create a document from a template with custom document'
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the custom document data
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));
const documentId = Number(page.url().split('/').pop());
const documentId = page.url().split('/').pop();
const document = await prisma.envelope.findFirstOrThrow({
where: {
secondaryId: mapDocumentIdToSecondaryId(documentId),
id: documentId,
},
include: {
envelopeItems: {
@ -343,7 +339,7 @@ test('[TEMPLATE]: should create a team document from a template with custom docu
await apiSignin({
page,
email: owner.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title
@ -387,13 +383,13 @@ test('[TEMPLATE]: should create a team document from a template with custom docu
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the custom document data
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));
const documentId = Number(page.url().split('/').pop());
const documentId = page.url().split('/').pop();
const document = await prisma.envelope.findFirstOrThrow({
where: {
secondaryId: mapDocumentIdToSecondaryId(documentId),
id: documentId,
},
include: {
envelopeItems: {
@ -438,7 +434,7 @@ test('[TEMPLATE]: should create a document from a template using template docume
await apiSignin({
page,
email: user.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title
@ -467,13 +463,13 @@ test('[TEMPLATE]: should create a document from a template using template docume
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the template's document data
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));
const documentId = Number(page.url().split('/').pop());
const documentId = page.url().split('/').pop();
const document = await prisma.envelope.findFirstOrThrow({
where: {
secondaryId: mapDocumentIdToSecondaryId(documentId),
id: documentId,
},
include: {
envelopeItems: {
@ -519,7 +515,7 @@ test('[TEMPLATE]: should persist document visibility when creating from template
await apiSignin({
page,
email: owner.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title and visibility
@ -558,13 +554,13 @@ test('[TEMPLATE]: should persist document visibility when creating from template
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the correct visibility
await page.waitForURL(new RegExp(`/t/${team.url}/documents/\\d+`));
await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));
const documentId = Number(page.url().split('/').pop());
const documentId = page.url().split('/').pop();
const document = await prisma.envelope.findFirstOrThrow({
where: {
secondaryId: mapDocumentIdToSecondaryId(documentId),
id: documentId,
},
include: {
envelopeItems: {

View File

@ -3,7 +3,6 @@ import { customAlphabet } from 'nanoid';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { createDocumentAuthOptions } from '@documenso/lib/utils/document-auth';
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedDirectTemplate, seedTemplate } from '@documenso/prisma/seed/templates';
@ -35,7 +34,7 @@ test('[DIRECT_TEMPLATES]: create direct link for template', async ({ page }) =>
redirectPath: `/t/${team.url}/templates`,
});
const url = `${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(teamTemplate.secondaryId)}`;
const url = `${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${teamTemplate.id}`;
// Owner should see list of templates with no direct link badge.
await page.goto(url);
@ -78,7 +77,7 @@ test('[DIRECT_TEMPLATES]: toggle direct template link', async ({ page }) => {
// Navigate to template settings and disable access.
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatTemplatesPath(template.team?.url)}`);
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
await page.getByRole('menuitem', { name: 'Direct link' }).click();
await page.getByTestId('template-direct-link').click();
await page.getByRole('switch').click();
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Direct link signing has been').first()).toBeVisible();
@ -112,7 +111,7 @@ test('[DIRECT_TEMPLATES]: delete direct template link', async ({ page }) => {
// Navigate to template settings and delete the access.
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}${formatTemplatesPath(template.team?.url)}`);
await page.getByRole('cell', { name: 'Use Template' }).getByRole('button').nth(1).click();
await page.getByRole('menuitem', { name: 'Direct link' }).click();
await page.getByTestId('template-direct-link').click();
await page.getByRole('button', { name: 'Remove' }).click();
await page.getByRole('button', { name: 'Confirm' }).click();
await expect(page.getByText('Direct template link deleted').first()).toBeVisible();
@ -172,6 +171,7 @@ test('[DIRECT_TEMPLATES]: use direct template link with 1 recipient', async ({ p
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.waitForTimeout(100);
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail());
await page.getByRole('button', { name: 'Continue' }).click();

View File

@ -1,7 +1,6 @@
import { expect, test } from '@playwright/test';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
import { seedUser } from '@documenso/prisma/seed/users';
@ -21,13 +20,11 @@ test.describe('Unauthorized Access to Templates', () => {
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
redirectPath: `/t/${team.url}/templates/${template.id}`,
});
await page.goto(
`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`,
);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${template.id}`);
await expect(page.getByRole('heading', { name: 'Team not found' })).toBeVisible();
});
test('should block unauthorized access to the template edit page', async ({ page }) => {
@ -39,12 +36,10 @@ test.describe('Unauthorized Access to Templates', () => {
await apiSignin({
page,
email: unauthorizedUser.email,
redirectPath: `/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
await page.goto(
`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/edit`,
);
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${template.id}/edit`);
await expect(page.getByRole('heading', { name: 'Team not found' })).toBeVisible();
});
});