chore: remove issue 2766 test changes

This commit is contained in:
ephraimduncan
2026-09-30 21:03:40 +00:00
parent b4f5238206
commit 917c698160
5 changed files with 0 additions and 495 deletions
@@ -1,85 +0,0 @@
import { nanoid } from '@documenso/lib/universal/id';
import { prisma } from '@documenso/prisma';
import { expect, test } from '@playwright/test';
import {
addEnvelopeItemPdf,
getEnvelopeEditorSettingsTrigger,
openDocumentEnvelopeEditor,
openEmbeddedEnvelopeEditor,
openTemplateEnvelopeEditor,
persistEmbeddedEnvelope,
setRecipientEmail,
type TEnvelopeEditorSurface,
} from '../fixtures/envelope-editor';
import { expectToastTextToBeVisible } from '../fixtures/generic';
const runAttachmentSettingsFlow = async (surface: TEnvelopeEditorSurface) => {
const { root, isEmbedded } = surface;
const externalId = `e2e-email-attachments-${nanoid()}`;
if (isEmbedded && !surface.envelopeId) {
await addEnvelopeItemPdf(root);
await setRecipientEmail(root, 0, 'signer@example.com');
}
for (const attachDocument of [false, true, false]) {
await getEnvelopeEditorSettingsTrigger(root).click();
await root.locator('input[name="externalId"]').fill(externalId);
await root.getByRole('button', { name: 'Notifications' }).click();
const attachmentControl = root.getByRole('checkbox', { name: 'Attach completed document PDFs to emails' });
await expect(attachmentControl).toBeChecked({ checked: !attachDocument });
await attachmentControl.setChecked(attachDocument);
await expect(root.locator('#documentCompleted')).toBeChecked();
await expect(root.locator('#ownerDocumentCompleted')).toBeChecked();
await root.getByRole('button', { name: 'Update', exact: true }).click();
if (!isEmbedded) {
await expectToastTextToBeVisible(root, 'Envelope updated');
const meta = await prisma.envelope.findUniqueOrThrow({
where: { id: surface.envelopeId },
include: { documentMeta: true },
});
expect(meta.documentMeta.emailSettings).toMatchObject({ attachDocument });
await root.reload();
}
}
if (isEmbedded) {
await persistEmbeddedEnvelope(surface);
}
const envelope = await prisma.envelope.findFirstOrThrow({
where: { externalId, userId: surface.userId, teamId: surface.teamId, type: surface.envelopeType },
include: { documentMeta: true },
});
expect(envelope.documentMeta.emailSettings).toMatchObject({
attachDocument: false,
documentCompleted: true,
ownerDocumentCompleted: true,
});
};
test.describe('document editor', () => {
test('save email attachment opt-out and toggle back', async ({ page }) => {
await runAttachmentSettingsFlow(await openDocumentEnvelopeEditor(page));
});
});
test.describe('template editor', () => {
test('save email attachment opt-out and toggle back', async ({ page }) => {
await runAttachmentSettingsFlow(await openTemplateEnvelopeEditor(page));
});
});
test.describe('embedded create', () => {
test('save email attachment opt-out and toggle back', async ({ page }) => {
await runAttachmentSettingsFlow(await openEmbeddedEnvelopeEditor(page, { envelopeType: 'DOCUMENT' }));
});
});
test.describe('embedded edit', () => {
test('save email attachment opt-out and toggle back', async ({ page }) => {
await runAttachmentSettingsFlow(await openEmbeddedEnvelopeEditor(page, { envelopeType: 'TEMPLATE', mode: 'edit' }));
});
});
@@ -215,8 +215,6 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
await page.getByRole('textbox', { name: 'Reply to email' }).click();
await page.getByRole('textbox', { name: 'Reply to email' }).fill('organisation@documenso.com');
await page.getByRole('checkbox', { name: 'Attach completed document PDFs to emails' }).uncheck();
// Update email document settings by enabling/disabling some checkboxes
await page.getByRole('checkbox', { name: 'Email the owner when a recipient signs' }).uncheck();
await page.getByRole('checkbox', { name: 'Email the signer if the document is still pending' }).uncheck();
@@ -232,7 +230,6 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
// Check that the team settings have inherited these values.
expect(teamSettings.emailReplyTo).toEqual('organisation@documenso.com');
expect(teamSettings.emailDocumentSettings).toEqual({
attachDocument: false,
recipientSigningRequest: true,
recipientRemoved: true,
recipientSigned: false, // unchecked
@@ -276,7 +273,6 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
// Check that the team settings have overridden the organisation values.
expect(updatedTeamSettings.emailReplyTo).toEqual('team@example.com');
expect(updatedTeamSettings.emailDocumentSettings).toEqual({
attachDocument: true,
recipientSigned: true,
recipientSigningRequest: false,
recipientRemoved: true,
@@ -299,7 +295,6 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
expect(teamOverrideDocumentMeta.emailReplyTo).toEqual('team@example.com');
expect(teamOverrideDocumentMeta.emailSettings).toEqual({
attachDocument: true,
recipientSigned: true,
recipientSigningRequest: false,
recipientRemoved: true,
@@ -327,7 +322,6 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
// Check that the team settings now inherit from organisation again.
expect(inheritedTeamSettings.emailReplyTo).toEqual('organisation@documenso.com');
expect(inheritedTeamSettings.emailDocumentSettings).toEqual({
attachDocument: false,
recipientSigningRequest: true,
recipientRemoved: true,
recipientSigned: false,
@@ -350,7 +344,6 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
expect(documentMeta.emailReplyTo).toEqual('organisation@documenso.com');
expect(documentMeta.emailSettings).toEqual({
attachDocument: false,
recipientSigningRequest: true,
recipientRemoved: true,
recipientSigned: false,
@@ -361,22 +354,4 @@ test('[ORGANISATIONS]: manage email preferences', async ({ page }) => {
ownerDocumentCompleted: true,
ownerDocumentCreated: true,
});
// Re-enable attachments and verify that inherited defaults change only for new documents.
await page.goto(`/o/${organisation.url}/settings/email`);
const attachmentControl = page.getByRole('checkbox', { name: 'Attach completed document PDFs to emails' });
await expect(attachmentControl).not.toBeChecked();
await attachmentControl.check();
await page.getByRole('button', { name: 'Save changes' }).first().click();
await expect(page.getByText('Your email preferences have been updated').first()).toBeVisible();
await page.reload();
await expect(attachmentControl).toBeChecked();
const restoredSettings = await getTeamSettings({ teamId: team.id });
expect(restoredSettings.emailDocumentSettings.attachDocument).toBe(true);
const newDocument = await seedTeamDocumentWithMeta(team);
const newMeta = await prisma.documentMeta.findUniqueOrThrow({ where: { id: newDocument.documentMetaId } });
expect(newMeta.emailSettings?.attachDocument).toBe(true);
const existingMeta = await prisma.documentMeta.findUniqueOrThrow({ where: { id: document.documentMetaId } });
expect(existingMeta.emailSettings?.attachDocument).toBe(false);
});
@@ -1,190 +0,0 @@
import { DocumentDistributionMethod, DocumentSource, RecipientRole } from '@prisma/client';
import type { ReactElement } from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { DEFAULT_DOCUMENT_EMAIL_SETTINGS } from '../../../types/document-email';
import type { JobRunIO } from '../../client/_internal/job';
import { run } from './send-document-completed-emails.handler';
const mocks = vi.hoisted(() => ({
findEnvelope: vi.fn(),
createAuditLog: vi.fn(),
findEnvelopeItems: vi.fn(),
getEmailContext: vi.fn(),
getFile: vi.fn(),
sendMail: vi.fn(),
checkLimits: vi.fn(),
}));
vi.mock('@documenso/prisma', () => ({
prisma: {
envelope: { findUnique: mocks.findEnvelope },
envelopeItem: { findMany: mocks.findEnvelopeItems },
documentAuditLog: { create: mocks.createAuditLog },
},
}));
vi.mock('@documenso/email/templates/document-completed', () => ({
DocumentCompletedEmailTemplate: () => null,
}));
vi.mock('../../../client-only/providers/i18n-server', () => ({
getI18nInstance: async () => ({ _: () => 'Signing Complete!' }),
}));
vi.mock('../../../constants/app', () => ({ NEXT_PUBLIC_WEBAPP_URL: () => 'http://localhost:3000' }));
vi.mock('../../../server-only/email/get-email-context', () => ({ getEmailContext: mocks.getEmailContext }));
vi.mock('../../../server-only/rate-limit/assert-organisation-rates-and-limits', () => ({
assertOrganisationRatesAndLimits: mocks.checkLimits,
}));
vi.mock('../../../universal/upload/get-file.server', () => ({ getFileServerSide: mocks.getFile }));
vi.mock('../../../utils/document-audit-logs', () => ({ createDocumentAuditLogData: (data: unknown) => data }));
vi.mock('../../../utils/teams', () => ({ formatDocumentsPath: () => '/t/test-team/documents' }));
vi.mock('../../../utils/render-email-with-i18n', () => ({
renderEmailWithI18N: async (template: ReactElement<{ downloadLink: string }>) => template.props.downloadLink,
}));
const createEnvelope = (distributionMethod: DocumentDistributionMethod = DocumentDistributionMethod.EMAIL) => ({
id: 'envelope_test',
secondaryId: 'document_1',
teamId: 1,
internalVersion: 2,
title: 'Confidential document',
source: DocumentSource.DOCUMENT,
team: { id: 1, url: 'test-team' },
user: { id: 1, name: 'Owner', email: 'owner@example.com', disabled: false },
documentMeta: {
distributionMethod,
emailSettings: { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS },
},
envelopeItems: [
{ title: 'First document', documentData: { id: 'file_1', type: 'S3', data: 'file_1' } },
{ title: 'Second document', documentData: { id: 'file_2', type: 'BYTES', data: 'file_2' } },
],
recipients: [
{ id: 2, name: 'Signer', email: 'signer@example.com', role: RecipientRole.SIGNER, token: 'signer-token' },
{ id: 3, name: 'CC', email: 'cc@example.com', role: RecipientRole.CC, token: 'cc-token' },
],
});
let envelope = createEnvelope();
const io = { logger: { warn: vi.fn() } } as unknown as JobRunIO;
const runJob = () => run({ payload: { envelopeId: envelope.id }, io });
beforeEach(() => {
vi.clearAllMocks();
envelope = createEnvelope();
mocks.findEnvelope.mockImplementation(async () => envelope);
mocks.findEnvelopeItems.mockImplementation(async () => envelope.envelopeItems);
mocks.getFile.mockResolvedValue(new Uint8Array([1, 2, 3]));
mocks.getEmailContext.mockResolvedValue({
branding: {},
emailLanguage: 'en',
senderEmail: 'sender@example.com',
replyToEmail: 'reply@example.com',
organisationId: 'org_test',
claims: {},
emailsDisabled: false,
emailTransport: { sendMail: mocks.sendMail },
});
});
describe('completion email document attachments', () => {
it('keeps email content, links, audit logs, and CC metering when PDFs are disabled', async () => {
envelope.documentMeta.emailSettings.attachDocument = false;
// Loading a PDF would fail: link-only email must still succeed.
mocks.getFile.mockRejectedValue(new Error('PDF storage unavailable'));
await runJob();
expect(mocks.findEnvelopeItems).not.toHaveBeenCalled();
expect(mocks.getFile).not.toHaveBeenCalled();
expect(mocks.sendMail).toHaveBeenCalledTimes(3);
const emails = mocks.sendMail.mock.calls.map(([email]) => email);
expect(emails.map((email) => email.to[0].address)).toEqual([
'owner@example.com',
'signer@example.com',
'cc@example.com',
]);
for (const email of emails) {
expect(email).toMatchObject({ attachments: [], subject: 'Signing Complete!' });
expect(email.html).toBe(email.text);
}
expect(emails[0].html).toBe('http://localhost:3000/t/test-team/documents/envelope_test');
expect(emails[1].html).toBe('http://localhost:3000/sign/signer-token/complete');
expect(emails[2].html).toBe('http://localhost:3000/sign/cc-token/complete');
expect(mocks.createAuditLog).toHaveBeenCalledTimes(3);
expect(mocks.checkLimits).toHaveBeenCalledOnce();
});
it.each([true, undefined])('attaches all PDFs by default and when enabled (%s)', async (attachDocument) => {
mocks.findEnvelope.mockResolvedValue({
...envelope,
documentMeta: { ...envelope.documentMeta, emailSettings: { attachDocument } },
});
await runJob();
expect(mocks.getFile).toHaveBeenCalledTimes(2);
expect(mocks.sendMail).toHaveBeenCalledTimes(3);
for (const [email] of mocks.sendMail.mock.calls) {
expect(email.attachments).toEqual([
{ filename: 'First document.pdf', content: Buffer.from([1, 2, 3]), contentType: 'application/pdf' },
{ filename: 'Second document.pdf', content: Buffer.from([1, 2, 3]), contentType: 'application/pdf' },
]);
}
});
it('retains legacy single-document filenames', async () => {
envelope.internalVersion = 1;
envelope.envelopeItems = [envelope.envelopeItems[0]];
await runJob();
expect(mocks.sendMail.mock.calls[0][0].attachments[0].filename).toBe('Confidential document.pdf');
});
it('sends link-only owner emails when recipient emails are disabled', async () => {
envelope.documentMeta.emailSettings.attachDocument = false;
envelope.documentMeta.emailSettings.documentCompleted = false;
await runJob();
expect(mocks.findEnvelopeItems).not.toHaveBeenCalled();
expect(mocks.getFile).not.toHaveBeenCalled();
expect(mocks.sendMail).toHaveBeenCalledOnce();
expect(mocks.sendMail.mock.calls[0][0].to[0].address).toBe('owner@example.com');
});
it('sends link-only recipient emails when owner emails are disabled', async () => {
envelope.documentMeta.emailSettings.attachDocument = false;
envelope.documentMeta.emailSettings.ownerDocumentCompleted = false;
await runJob();
expect(mocks.findEnvelopeItems).not.toHaveBeenCalled();
expect(mocks.getFile).not.toHaveBeenCalled();
expect(mocks.sendMail).toHaveBeenCalledTimes(2);
});
it('sends a single link-only completion email when the owner is also a recipient', async () => {
envelope.documentMeta.emailSettings.attachDocument = false;
envelope.recipients[0].email = envelope.user.email;
await runJob();
expect(mocks.sendMail).toHaveBeenCalledTimes(2);
expect(mocks.sendMail.mock.calls[0][0]).toMatchObject({
to: [{ address: 'owner@example.com' }],
attachments: [],
html: 'http://localhost:3000/t/test-team/documents/envelope_test',
});
});
it('preserves owner-only link emails when distribution is NONE', async () => {
envelope.documentMeta.distributionMethod = DocumentDistributionMethod.NONE;
envelope.documentMeta.emailSettings.attachDocument = false;
await runJob();
expect(mocks.findEnvelopeItems).not.toHaveBeenCalled();
expect(mocks.getFile).not.toHaveBeenCalled();
expect(mocks.sendMail).toHaveBeenCalledOnce();
});
it('does not load PDFs or send mail when both completion emails are disabled', async () => {
envelope.documentMeta.emailSettings.documentCompleted = false;
envelope.documentMeta.emailSettings.ownerDocumentCompleted = false;
await runJob();
expect(mocks.findEnvelopeItems).not.toHaveBeenCalled();
expect(mocks.getFile).not.toHaveBeenCalled();
expect(mocks.sendMail).not.toHaveBeenCalled();
});
});
@@ -1,101 +0,0 @@
import { describe, expect, it } from 'vitest';
import { ZCreateDocumentMutationSchema, ZGenerateDocumentFromTemplateMutationSchema } from '../../api/v1/schema';
import { ZCreateDocumentPayloadSchema } from '../../trpc/server/document-router/create-document.types';
import { ZCreateEnvelopePayloadSchema } from '../../trpc/server/envelope-router/create-envelope.types';
import { ZUpdateEnvelopeRequestSchema } from '../../trpc/server/envelope-router/update-envelope.types';
import { ZUseEnvelopePayloadSchema } from '../../trpc/server/envelope-router/use-envelope.types';
import { ZUpdateOrganisationSettingsRequestSchema } from '../../trpc/server/organisation-router/update-organisation-settings.types';
import { ZUpdateTeamSettingsRequestSchema } from '../../trpc/server/team-router/update-team-settings.types';
import {
ZCreateDocumentFromTemplateRequestSchema,
ZCreateTemplateV2RequestSchema,
} from '../../trpc/server/template-router/schema';
const requestCases = [
{
name: 'v1 document creation',
schema: ZCreateDocumentMutationSchema,
body: { title: 'Document', recipients: [] },
field: 'meta',
},
{
name: 'v1 template use',
schema: ZGenerateDocumentFromTemplateMutationSchema,
body: { recipients: [] },
field: 'meta',
},
{ name: 'document creation', schema: ZCreateDocumentPayloadSchema, body: { title: 'Document' }, field: 'meta' },
{
name: 'envelope creation',
schema: ZCreateEnvelopePayloadSchema,
body: { title: 'Document', type: 'DOCUMENT' },
field: 'meta',
},
{
name: 'envelope update',
schema: ZUpdateEnvelopeRequestSchema,
body: { envelopeId: 'envelope_test' },
field: 'meta',
},
{
name: 'envelope template use',
schema: ZUseEnvelopePayloadSchema,
body: { envelopeId: 'envelope_test' },
field: 'override',
},
{ name: 'template creation', schema: ZCreateTemplateV2RequestSchema, body: { title: 'Template' }, field: 'meta' },
{
name: 'document from template',
schema: ZCreateDocumentFromTemplateRequestSchema,
body: { templateId: 1, recipients: [] },
field: 'override',
},
] as const;
describe.each(requestCases)('$name email settings API', ({ schema, body, field }) => {
it.each([true, false])('accepts and retains attachDocument=%s', (attachDocument) => {
const request = { ...body, [field]: { emailSettings: { attachDocument } } };
const result = schema.parse(request);
expect(result).toMatchObject({ [field]: { emailSettings: { attachDocument } } });
});
it('rejects non-boolean attachment controls instead of enabling attachments', () => {
const request = { ...body, [field]: { emailSettings: { attachDocument: 'false' } } };
expect(schema.safeParse(request).success).toBe(false);
});
});
describe('default email settings API', () => {
it.each([true, false])('retains organisation and team attachment defaults (%s)', (attachDocument) => {
expect(
ZUpdateOrganisationSettingsRequestSchema.parse({
organisationId: 'org_test',
data: { emailDocumentSettings: { attachDocument } },
}),
).toMatchObject({ data: { emailDocumentSettings: { attachDocument } } });
expect(
ZUpdateTeamSettingsRequestSchema.parse({ teamId: 1, data: { emailDocumentSettings: { attachDocument } } }),
).toMatchObject({ data: { emailDocumentSettings: { attachDocument } } });
});
it('rejects malformed organisation and team defaults', () => {
expect(
ZUpdateOrganisationSettingsRequestSchema.safeParse({
organisationId: 'org_test',
data: { emailDocumentSettings: { attachDocument: 'false' } },
}).success,
).toBe(false);
expect(
ZUpdateTeamSettingsRequestSchema.safeParse({
teamId: 1,
data: { emailDocumentSettings: { attachDocument: 'false' } },
}).success,
).toBe(false);
});
it('retains null as the team inheritance control', () => {
expect(ZUpdateTeamSettingsRequestSchema.parse({ teamId: 1, data: { emailDocumentSettings: null } })).toMatchObject({
data: { emailDocumentSettings: null },
});
});
});
-94
View File
@@ -1,94 +0,0 @@
import { DocumentDistributionMethod, type DocumentMeta } from '@prisma/client';
import { describe, expect, it } from 'vitest';
import { extractDerivedDocumentMeta } from '../utils/document';
import { generateDefaultOrganisationSettings } from '../utils/organisations';
import { extractDerivedTeamSettings, generateDefaultTeamSettings } from '../utils/teams';
import {
DEFAULT_DOCUMENT_EMAIL_SETTINGS,
extractDerivedDocumentEmailSettings,
ZDocumentEmailSettingsInputSchema,
ZDocumentEmailSettingsSchema,
} from './document-email';
import { ZDocumentMetaCreateSchema, ZDocumentMetaUpdateSchema } from './document-meta';
describe('document email attachments', () => {
it('keeps attachments enabled for absent and legacy settings', () => {
expect(extractDerivedDocumentEmailSettings().attachDocument).toBe(true);
expect(ZDocumentEmailSettingsSchema.parse(null).attachDocument).toBe(true);
expect(ZDocumentEmailSettingsInputSchema.parse({ documentCompleted: false })).toMatchObject({
attachDocument: true,
documentCompleted: false,
});
});
it('disables attachments independently of notification events', () => {
expect(ZDocumentEmailSettingsInputSchema.parse({ attachDocument: false })).toEqual({
...DEFAULT_DOCUMENT_EMAIL_SETTINGS,
attachDocument: false,
});
});
it.each(['false', 0, null, [], {}])('rejects invalid attachment values: %j', (attachDocument) => {
expect(ZDocumentEmailSettingsInputSchema.safeParse({ attachDocument }).success).toBe(false);
expect(ZDocumentMetaCreateSchema.safeParse({ emailSettings: { attachDocument } }).success).toBe(false);
expect(ZDocumentMetaUpdateSchema.safeParse({ emailSettings: { attachDocument } }).success).toBe(false);
});
it('does not replace an explicit opt-out with defaults when another input is invalid', () => {
expect(
ZDocumentMetaCreateSchema.safeParse({
emailSettings: { attachDocument: false, documentCompleted: 'true' },
}).success,
).toBe(false);
});
it('preserves the owner attachment setting when email distribution is disabled', () => {
const settings = extractDerivedDocumentEmailSettings({
distributionMethod: DocumentDistributionMethod.NONE,
emailSettings: { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS, attachDocument: false },
} as DocumentMeta);
expect(settings).toMatchObject({
attachDocument: false,
documentCompleted: false,
ownerDocumentCompleted: true,
});
});
it('inherits organisation defaults, allows team overrides, and restores inheritance', () => {
const organisationSettings = generateDefaultOrganisationSettings();
organisationSettings.emailDocumentSettings = { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS, attachDocument: false };
const teamSettings = generateDefaultTeamSettings();
const inherited = extractDerivedTeamSettings(organisationSettings, teamSettings);
expect(extractDerivedDocumentMeta(inherited, undefined).emailSettings.attachDocument).toBe(false);
teamSettings.emailDocumentSettings = { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS, attachDocument: true };
const overridden = extractDerivedTeamSettings(organisationSettings, teamSettings);
expect(extractDerivedDocumentMeta(overridden, undefined).emailSettings.attachDocument).toBe(true);
teamSettings.emailDocumentSettings = null;
expect(
extractDerivedDocumentMeta(extractDerivedTeamSettings(organisationSettings, teamSettings), undefined)
.emailSettings.attachDocument,
).toBe(false);
});
it('allows a document or template to override inherited attachment defaults in either direction', () => {
const settings = generateDefaultOrganisationSettings();
expect(
extractDerivedDocumentMeta(settings, {
emailSettings: { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS, attachDocument: false },
}).emailSettings.attachDocument,
).toBe(false);
settings.emailDocumentSettings = { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS, attachDocument: false };
expect(
extractDerivedDocumentMeta(settings, {
emailSettings: { ...DEFAULT_DOCUMENT_EMAIL_SETTINGS, attachDocument: true },
}).emailSettings.attachDocument,
).toBe(true);
});
});