mirror of
https://github.com/documenso/documenso.git
synced 2026-07-13 22:37:24 +10:00
322 lines
10 KiB
TypeScript
322 lines
10 KiB
TypeScript
import { nanoid } from '@documenso/lib/universal/id';
|
|
import { prisma } from '@documenso/prisma';
|
|
import { expect, type Page, test } from '@playwright/test';
|
|
|
|
import {
|
|
addEnvelopeItemPdf,
|
|
getEnvelopeEditorSettingsTrigger,
|
|
openDocumentEnvelopeEditor,
|
|
openEmbeddedEnvelopeEditor,
|
|
openTemplateEnvelopeEditor,
|
|
persistEmbeddedEnvelope,
|
|
type TEnvelopeEditorSurface,
|
|
} from '../fixtures/envelope-editor';
|
|
import { expectToastTextToBeVisible } from '../fixtures/generic';
|
|
|
|
const TEST_ATTACHMENTS = {
|
|
first: {
|
|
label: 'E2E First Attachment',
|
|
url: 'https://example.com/first-attachment',
|
|
},
|
|
second: {
|
|
label: 'E2E Second Attachment',
|
|
url: 'https://example.com/second-attachment',
|
|
},
|
|
third: {
|
|
label: 'E2E Third Attachment',
|
|
url: 'https://example.com/third-attachment',
|
|
},
|
|
};
|
|
|
|
type AttachmentFlowResult = {
|
|
externalId: string;
|
|
expectedAttachments: Array<{ label: string; url: string }>;
|
|
deletedAttachment: { label: string; url: string };
|
|
};
|
|
|
|
const openSettingsDialog = async (root: Page) => {
|
|
await getEnvelopeEditorSettingsTrigger(root).click();
|
|
await expect(root.getByRole('heading', { name: 'Document Settings' })).toBeVisible();
|
|
};
|
|
|
|
const updateExternalId = async (surface: TEnvelopeEditorSurface, externalId: string) => {
|
|
await openSettingsDialog(surface.root);
|
|
await surface.root.locator('input[name="externalId"]').fill(externalId);
|
|
await surface.root.getByRole('button', { name: 'Update' }).click();
|
|
|
|
if (!surface.isEmbedded) {
|
|
await expectToastTextToBeVisible(surface.root, 'Envelope updated');
|
|
}
|
|
};
|
|
|
|
const openAttachmentsPopover = async (root: Page) => {
|
|
await root.getByRole('button', { name: 'Attachments' }).click();
|
|
|
|
await expect(root.getByRole('heading', { name: 'Attachments', level: 4 })).toBeVisible();
|
|
};
|
|
|
|
const addAttachment = async (root: Page, { label, url }: { label: string; url: string }) => {
|
|
await root.getByRole('button', { name: 'Add Attachment' }).click();
|
|
await root.getByPlaceholder('Label').fill(label);
|
|
await root.getByPlaceholder('URL').fill(url);
|
|
await root.getByRole('button', { name: 'Add', exact: true }).click();
|
|
};
|
|
|
|
const getAttachmentItems = (root: Page) => root.locator('.rounded-md.border.border-border.p-2');
|
|
|
|
const getAttachmentDeleteButtons = (root: Page) => getAttachmentItems(root).locator('button');
|
|
|
|
const assertAttachmentVisibleInPopover = async (root: Page, { label, url }: { label: string; url: string }) => {
|
|
const items = getAttachmentItems(root);
|
|
const matchingItem = items.filter({ hasText: label });
|
|
|
|
await expect(matchingItem).toBeVisible();
|
|
await expect(matchingItem.locator(`a[href="${url}"]`)).toBeVisible();
|
|
};
|
|
|
|
const assertAttachmentNotVisibleInPopover = async (root: Page, label: string) => {
|
|
await expect(getAttachmentItems(root).filter({ hasText: label })).toHaveCount(0);
|
|
};
|
|
|
|
const assertAttachmentCount = async (root: Page, count: number) => {
|
|
const button = root.getByRole('button', { name: 'Attachments' });
|
|
|
|
if (count > 0) {
|
|
await expect(button).toContainText(`(${count})`);
|
|
} else {
|
|
await expect(button).not.toContainText('(');
|
|
}
|
|
};
|
|
|
|
const assertAttachmentsInDatabase = async ({
|
|
externalId,
|
|
surface,
|
|
expectedAttachments,
|
|
deletedLabel,
|
|
}: {
|
|
externalId: string;
|
|
surface: TEnvelopeEditorSurface;
|
|
expectedAttachments: Array<{ label: string; url: string }>;
|
|
deletedLabel?: string;
|
|
}) => {
|
|
const envelope = await prisma.envelope.findFirstOrThrow({
|
|
where: {
|
|
externalId,
|
|
userId: surface.userId,
|
|
teamId: surface.teamId,
|
|
type: surface.envelopeType,
|
|
},
|
|
include: {
|
|
envelopeAttachments: {
|
|
orderBy: {
|
|
createdAt: 'asc',
|
|
},
|
|
},
|
|
},
|
|
orderBy: {
|
|
createdAt: 'desc',
|
|
},
|
|
});
|
|
|
|
expect(envelope.envelopeAttachments).toHaveLength(expectedAttachments.length);
|
|
|
|
for (const expected of expectedAttachments) {
|
|
const found = envelope.envelopeAttachments.find((a) => a.label === expected.label);
|
|
|
|
expect(found).toBeDefined();
|
|
expect(found!.data).toBe(expected.url);
|
|
expect(found!.type).toBe('link');
|
|
}
|
|
|
|
if (deletedLabel) {
|
|
expect(envelope.envelopeAttachments.some((a) => a.label === deletedLabel)).toBe(false);
|
|
}
|
|
};
|
|
|
|
const runAttachmentFlow = async (surface: TEnvelopeEditorSurface): Promise<AttachmentFlowResult> => {
|
|
const externalId = `e2e-attachments-${nanoid()}`;
|
|
|
|
await updateExternalId(surface, externalId);
|
|
await openAttachmentsPopover(surface.root);
|
|
|
|
// Create first attachment.
|
|
await addAttachment(surface.root, TEST_ATTACHMENTS.first);
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment added successfully.');
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.first);
|
|
await assertAttachmentCount(surface.root, 1);
|
|
|
|
await assertAttachmentsInDatabase({
|
|
externalId,
|
|
surface,
|
|
expectedAttachments: [TEST_ATTACHMENTS.first],
|
|
});
|
|
|
|
// Create second attachment.
|
|
await addAttachment(surface.root, TEST_ATTACHMENTS.second);
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment added successfully.');
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.second);
|
|
await assertAttachmentCount(surface.root, 2);
|
|
|
|
await assertAttachmentsInDatabase({
|
|
externalId,
|
|
surface,
|
|
expectedAttachments: [TEST_ATTACHMENTS.first, TEST_ATTACHMENTS.second],
|
|
});
|
|
|
|
// Create third attachment.
|
|
await addAttachment(surface.root, TEST_ATTACHMENTS.third);
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment added successfully.');
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.third);
|
|
await assertAttachmentCount(surface.root, 3);
|
|
|
|
await assertAttachmentsInDatabase({
|
|
externalId,
|
|
surface,
|
|
expectedAttachments: [TEST_ATTACHMENTS.first, TEST_ATTACHMENTS.second, TEST_ATTACHMENTS.third],
|
|
});
|
|
|
|
// Delete first attachment.
|
|
await getAttachmentDeleteButtons(surface.root).first().click();
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment removed successfully.');
|
|
|
|
await expect(getAttachmentItems(surface.root)).toHaveCount(2);
|
|
await assertAttachmentNotVisibleInPopover(surface.root, TEST_ATTACHMENTS.first.label);
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.second);
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.third);
|
|
await assertAttachmentCount(surface.root, 2);
|
|
|
|
await assertAttachmentsInDatabase({
|
|
externalId,
|
|
surface,
|
|
expectedAttachments: [TEST_ATTACHMENTS.second, TEST_ATTACHMENTS.third],
|
|
deletedLabel: TEST_ATTACHMENTS.first.label,
|
|
});
|
|
|
|
return {
|
|
externalId,
|
|
expectedAttachments: [TEST_ATTACHMENTS.second, TEST_ATTACHMENTS.third],
|
|
deletedAttachment: TEST_ATTACHMENTS.first,
|
|
};
|
|
};
|
|
|
|
const runEmbeddedAttachmentFlow = async (surface: TEnvelopeEditorSurface): Promise<AttachmentFlowResult> => {
|
|
const externalId = `e2e-attachments-${nanoid()}`;
|
|
|
|
await updateExternalId(surface, externalId);
|
|
await openAttachmentsPopover(surface.root);
|
|
|
|
// Create first attachment.
|
|
await addAttachment(surface.root, TEST_ATTACHMENTS.first);
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment added successfully.');
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.first);
|
|
await assertAttachmentCount(surface.root, 1);
|
|
|
|
// Create second attachment.
|
|
await addAttachment(surface.root, TEST_ATTACHMENTS.second);
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment added successfully.');
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.second);
|
|
await assertAttachmentCount(surface.root, 2);
|
|
|
|
// Create third attachment.
|
|
await addAttachment(surface.root, TEST_ATTACHMENTS.third);
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment added successfully.');
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.third);
|
|
await assertAttachmentCount(surface.root, 3);
|
|
|
|
// Delete first attachment.
|
|
await getAttachmentDeleteButtons(surface.root).first().click();
|
|
await expectToastTextToBeVisible(surface.root, 'Attachment removed successfully.');
|
|
|
|
await expect(getAttachmentItems(surface.root)).toHaveCount(2);
|
|
await assertAttachmentNotVisibleInPopover(surface.root, TEST_ATTACHMENTS.first.label);
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.second);
|
|
await assertAttachmentVisibleInPopover(surface.root, TEST_ATTACHMENTS.third);
|
|
await assertAttachmentCount(surface.root, 2);
|
|
|
|
return {
|
|
externalId,
|
|
expectedAttachments: [TEST_ATTACHMENTS.second, TEST_ATTACHMENTS.third],
|
|
deletedAttachment: TEST_ATTACHMENTS.first,
|
|
};
|
|
};
|
|
|
|
const assertAttachmentsPersistedInDatabase = async ({
|
|
surface,
|
|
externalId,
|
|
expectedAttachments,
|
|
deletedAttachment,
|
|
}: {
|
|
surface: TEnvelopeEditorSurface;
|
|
externalId: string;
|
|
expectedAttachments: AttachmentFlowResult['expectedAttachments'];
|
|
deletedAttachment: AttachmentFlowResult['deletedAttachment'];
|
|
}) => {
|
|
await assertAttachmentsInDatabase({
|
|
externalId,
|
|
surface,
|
|
expectedAttachments,
|
|
deletedLabel: deletedAttachment.label,
|
|
});
|
|
};
|
|
|
|
test.describe('document editor', () => {
|
|
test('add, verify and delete attachments', async ({ page }) => {
|
|
const surface = await openDocumentEnvelopeEditor(page);
|
|
const result = await runAttachmentFlow(surface);
|
|
|
|
await assertAttachmentsPersistedInDatabase({
|
|
surface,
|
|
...result,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe('template editor', () => {
|
|
test('add, verify and delete attachments', async ({ page }) => {
|
|
const surface = await openTemplateEnvelopeEditor(page);
|
|
const result = await runAttachmentFlow(surface);
|
|
|
|
await assertAttachmentsPersistedInDatabase({
|
|
surface,
|
|
...result,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe('embedded create', () => {
|
|
test('add, verify and delete attachments', async ({ page }) => {
|
|
const surface = await openEmbeddedEnvelopeEditor(page, {
|
|
envelopeType: 'DOCUMENT',
|
|
tokenNamePrefix: 'e2e-embed-attachments',
|
|
});
|
|
|
|
await addEnvelopeItemPdf(surface.root, 'embedded-document-attachments.pdf');
|
|
|
|
const result = await runEmbeddedAttachmentFlow(surface);
|
|
await persistEmbeddedEnvelope(surface);
|
|
|
|
await assertAttachmentsPersistedInDatabase({
|
|
surface,
|
|
...result,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe('embedded edit', () => {
|
|
test('add, verify and delete attachments', async ({ page }) => {
|
|
const surface = await openEmbeddedEnvelopeEditor(page, {
|
|
envelopeType: 'TEMPLATE',
|
|
mode: 'edit',
|
|
tokenNamePrefix: 'e2e-embed-attachments',
|
|
});
|
|
|
|
const result = await runEmbeddedAttachmentFlow(surface);
|
|
await persistEmbeddedEnvelope(surface);
|
|
|
|
await assertAttachmentsPersistedInDatabase({
|
|
surface,
|
|
...result,
|
|
});
|
|
});
|
|
});
|