mirror of
https://github.com/documenso/documenso.git
synced 2026-07-27 18:35:05 +10:00
feat: allow editing pending envelope titles (#2604)
This commit is contained in:
@@ -0,0 +1,519 @@
|
||||
import { type APIRequestContext, expect, test } from '@playwright/test';
|
||||
import type { Team, User } from '@prisma/client';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentStatus, EnvelopeType, FieldType, RecipientRole } from '@documenso/prisma/client';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import type {
|
||||
TCreateEnvelopePayload,
|
||||
TCreateEnvelopeResponse,
|
||||
} from '@documenso/trpc/server/envelope-router/create-envelope.types';
|
||||
import type { TDistributeEnvelopeRequest } from '@documenso/trpc/server/envelope-router/distribute-envelope.types';
|
||||
import type { TCreateEnvelopeRecipientsRequest } from '@documenso/trpc/server/envelope-router/envelope-recipients/create-envelope-recipients.types';
|
||||
import type { TGetEnvelopeResponse } from '@documenso/trpc/server/envelope-router/get-envelope.types';
|
||||
import type { TUpdateEnvelopeItemsRequest } from '@documenso/trpc/server/envelope-router/update-envelope-items.types';
|
||||
|
||||
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
|
||||
const baseUrl = `${WEBAPP_BASE_URL}/api/v2-beta`;
|
||||
|
||||
test.describe.configure({
|
||||
mode: 'parallel',
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const createEnvelope = async (request: APIRequestContext, authToken: string) => {
|
||||
const payload: TCreateEnvelopePayload = {
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
title: 'Update Items Test',
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
|
||||
const pdfData = fs.readFileSync(path.join(__dirname, '../../../../../assets/example.pdf'));
|
||||
formData.append('files', new File([pdfData], 'test.pdf', { type: 'application/pdf' }));
|
||||
|
||||
const res = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
multipart: formData,
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
|
||||
return (await res.json()) as TCreateEnvelopeResponse;
|
||||
};
|
||||
|
||||
const getEnvelope = async (request: APIRequestContext, authToken: string, envelopeId: string) => {
|
||||
const res = await request.get(`${baseUrl}/envelope/${envelopeId}`, {
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
|
||||
return (await res.json()) as TGetEnvelopeResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transition an envelope from DRAFT to PENDING by adding a recipient with a
|
||||
* signature field and distributing.
|
||||
*/
|
||||
const distributeEnvelope = async (
|
||||
request: APIRequestContext,
|
||||
authToken: string,
|
||||
envelopeId: string,
|
||||
) => {
|
||||
const recipientEmail = `signer-${Date.now()}@test.documenso.com`;
|
||||
|
||||
// Create a SIGNER recipient.
|
||||
const recipientsRes = await request.post(`${baseUrl}/envelope/recipient/create-many`, {
|
||||
headers: { Authorization: `Bearer ${authToken}`, 'Content-Type': 'application/json' },
|
||||
data: {
|
||||
envelopeId,
|
||||
data: [
|
||||
{
|
||||
email: recipientEmail,
|
||||
name: 'Test Signer',
|
||||
role: RecipientRole.SIGNER,
|
||||
accessAuth: [],
|
||||
actionAuth: [],
|
||||
},
|
||||
],
|
||||
} satisfies TCreateEnvelopeRecipientsRequest,
|
||||
});
|
||||
|
||||
expect(recipientsRes.ok()).toBeTruthy();
|
||||
|
||||
const recipients = (await recipientsRes.json()).data;
|
||||
|
||||
// Resolve the envelope item ID.
|
||||
const envelope = await getEnvelope(request, authToken, envelopeId);
|
||||
const envelopeItemId = envelope.envelopeItems[0].id;
|
||||
|
||||
// Create a SIGNATURE field.
|
||||
const fieldsRes = await request.post(`${baseUrl}/envelope/field/create-many`, {
|
||||
headers: { Authorization: `Bearer ${authToken}`, 'Content-Type': 'application/json' },
|
||||
data: {
|
||||
envelopeId,
|
||||
data: [
|
||||
{
|
||||
recipientId: recipients[0].id,
|
||||
envelopeItemId,
|
||||
type: FieldType.SIGNATURE,
|
||||
page: 1,
|
||||
positionX: 100,
|
||||
positionY: 100,
|
||||
width: 50,
|
||||
height: 50,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(fieldsRes.ok()).toBeTruthy();
|
||||
|
||||
// Distribute.
|
||||
const distributeRes = await request.post(`${baseUrl}/envelope/distribute`, {
|
||||
headers: { Authorization: `Bearer ${authToken}`, 'Content-Type': 'application/json' },
|
||||
data: {
|
||||
envelopeId,
|
||||
} satisfies TDistributeEnvelopeRequest,
|
||||
});
|
||||
|
||||
expect(distributeRes.ok()).toBeTruthy();
|
||||
};
|
||||
|
||||
const updateEnvelopeItems = async (
|
||||
request: APIRequestContext,
|
||||
authToken: string,
|
||||
payload: TUpdateEnvelopeItemsRequest,
|
||||
) => {
|
||||
return request.post(`${baseUrl}/envelope/item/update-many`, {
|
||||
headers: { Authorization: `Bearer ${authToken}`, 'Content-Type': 'application/json' },
|
||||
data: payload,
|
||||
});
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('Update envelope items', () => {
|
||||
let user: User;
|
||||
let team: Team;
|
||||
let token: string;
|
||||
|
||||
test.beforeEach(async () => {
|
||||
({ user, team } = await seedUser());
|
||||
({ token } = await createApiToken({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
tokenName: 'test-update-items',
|
||||
expiresIn: null,
|
||||
}));
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// DRAFT envelope — full edit allowed
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should allow updating item title on a DRAFT envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: 'New Draft Title' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.title).toBe('New Draft Title');
|
||||
});
|
||||
|
||||
test('should allow updating item order on a DRAFT envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, order: 5 }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.order).toBe(5);
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// PENDING envelope — title-only edit allowed
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should allow updating item title on a PENDING envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: 'Updated Pending Title' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.title).toBe('Updated Pending Title');
|
||||
});
|
||||
|
||||
test('should allow title-only update when order matches existing on a PENDING envelope', async ({
|
||||
request,
|
||||
}) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
// Send the same order value that already exists — this should be treated as title-only.
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [
|
||||
{
|
||||
envelopeItemId: envelopeItem.id,
|
||||
title: 'Title With Same Order',
|
||||
order: envelopeItem.order,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.title).toBe('Title With Same Order');
|
||||
});
|
||||
|
||||
test('should create an ENVELOPE_ITEM_UPDATED audit log when updating item title on PENDING envelope', async ({
|
||||
request,
|
||||
}) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
const originalTitle = envelopeItem.title;
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: 'Audited Title' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
|
||||
const auditLog = await prisma.documentAuditLog.findFirst({
|
||||
where: {
|
||||
envelopeId: envelope.id,
|
||||
type: 'ENVELOPE_ITEM_UPDATED',
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
|
||||
expect(auditLog).not.toBeNull();
|
||||
|
||||
const auditData = auditLog!.data as Record<string, unknown>;
|
||||
|
||||
expect(auditData.envelopeItemId).toBe(envelopeItem.id);
|
||||
|
||||
const changes = auditData.changes as Array<{ field: string; from: string; to: string }>;
|
||||
|
||||
expect(changes).toHaveLength(1);
|
||||
expect(changes[0].field).toBe('title');
|
||||
expect(changes[0].from).toBe(originalTitle);
|
||||
expect(changes[0].to).toBe('Audited Title');
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// PENDING envelope — order change blocked
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should reject order change on a PENDING envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, order: 99 }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
});
|
||||
|
||||
test('should reject combined title and order change on a PENDING envelope', async ({
|
||||
request,
|
||||
}) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [
|
||||
{
|
||||
envelopeItemId: envelopeItem.id,
|
||||
title: 'Should Not Save',
|
||||
order: 99,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
|
||||
// Verify title was NOT changed.
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.title).not.toBe('Should Not Save');
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// COMPLETED envelope — all edits blocked
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should reject title update on a COMPLETED envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
// Transition to COMPLETED directly via database.
|
||||
await prisma.envelope.update({
|
||||
where: { id: envelope.id },
|
||||
data: { status: DocumentStatus.COMPLETED, completedAt: new Date() },
|
||||
});
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: 'Should Not Save' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
|
||||
// Verify title was NOT changed.
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.title).not.toBe('Should Not Save');
|
||||
});
|
||||
|
||||
test('should reject order update on a COMPLETED envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
await prisma.envelope.update({
|
||||
where: { id: envelope.id },
|
||||
data: { status: DocumentStatus.COMPLETED, completedAt: new Date() },
|
||||
});
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, order: 99 }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// REJECTED envelope — all edits blocked
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should reject title update on a REJECTED envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
// Transition to REJECTED directly via database.
|
||||
await prisma.envelope.update({
|
||||
where: { id: envelope.id },
|
||||
data: { status: DocumentStatus.REJECTED },
|
||||
});
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: 'Should Not Save' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
|
||||
// Verify title was NOT changed.
|
||||
const dbItem = await prisma.envelopeItem.findUniqueOrThrow({
|
||||
where: { id: envelopeItem.id },
|
||||
});
|
||||
|
||||
expect(dbItem.title).not.toBe('Should Not Save');
|
||||
});
|
||||
|
||||
test('should reject order update on a REJECTED envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
await prisma.envelope.update({
|
||||
where: { id: envelope.id },
|
||||
data: { status: DocumentStatus.REJECTED },
|
||||
});
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, order: 99 }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(400);
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Deleted envelope — all edits blocked
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should reject title update on a deleted envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
await distributeEnvelope(request, token, envelope.id);
|
||||
|
||||
// Soft-delete the envelope.
|
||||
await prisma.envelope.update({
|
||||
where: { id: envelope.id },
|
||||
data: { deletedAt: new Date() },
|
||||
});
|
||||
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: 'Should Not Save' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Validation edge cases
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
test('should reject an empty title on any envelope', async ({ request }) => {
|
||||
const envelope = await createEnvelope(request, token);
|
||||
const envelopeData = await getEnvelope(request, token, envelope.id);
|
||||
const envelopeItem = envelopeData.envelopeItems[0];
|
||||
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelope.id,
|
||||
data: [{ envelopeItemId: envelopeItem.id, title: '' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should reject update for an envelope item that does not belong to the envelope', async ({
|
||||
request,
|
||||
}) => {
|
||||
const envelopeA = await createEnvelope(request, token);
|
||||
const envelopeB = await createEnvelope(request, token);
|
||||
|
||||
const envelopeBData = await getEnvelope(request, token, envelopeB.id);
|
||||
const envelopeBItem = envelopeBData.envelopeItems[0];
|
||||
|
||||
// Try to update envelopeB's item via envelopeA's ID.
|
||||
const res = await updateEnvelopeItems(request, token, {
|
||||
envelopeId: envelopeA.id,
|
||||
data: [{ envelopeItemId: envelopeBItem.id, title: 'Cross Envelope' }],
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,10 @@ import path from 'node:path';
|
||||
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { seedPendingDocument } from '@documenso/prisma/seed/documents';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
import {
|
||||
type TEnvelopeEditorSurface,
|
||||
clickEnvelopeEditorStep,
|
||||
@@ -314,3 +317,72 @@ test.describe('embedded edit', () => {
|
||||
expect(items[0].order).toBe(2); // Expect order 2 because deleting items does not drop the order of sequential items.
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('pending envelope title editing', () => {
|
||||
test('edit envelope and item titles on a pending envelope', async ({ page }) => {
|
||||
const recipientEmail = `recipient-${nanoid()}@test.documenso.com`;
|
||||
const { user, team } = await seedUser();
|
||||
const pendingDocument = await seedPendingDocument(user, team.id, [recipientEmail], {
|
||||
internalVersion: 2,
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/documents/${pendingDocument.id}/edit`,
|
||||
});
|
||||
|
||||
// Verify the envelope editor loaded with the upload step visible.
|
||||
await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible();
|
||||
|
||||
// Edit the envelope title in the header.
|
||||
const envelopeTitleInput = page.locator('[data-testid="envelope-title-input"]');
|
||||
await expect(envelopeTitleInput).toBeEnabled();
|
||||
await envelopeTitleInput.fill('Updated Pending Title');
|
||||
|
||||
// Edit the envelope item title.
|
||||
const itemTitleInput = getEnvelopeItemTitleInputs(page).first();
|
||||
await expect(itemTitleInput).toBeEnabled();
|
||||
await itemTitleInput.fill('Updated Item Title');
|
||||
|
||||
// Wait for debounced auto-save to persist by navigating away and back.
|
||||
await clickEnvelopeEditorStep(page, 'addFields');
|
||||
await expect(page.getByText('Selected Recipient')).toBeVisible();
|
||||
await clickEnvelopeEditorStep(page, 'upload');
|
||||
await expect(page.getByRole('heading', { name: 'Documents' })).toBeVisible();
|
||||
|
||||
// Verify the titles persisted in the database.
|
||||
const updatedEnvelope = await prisma.envelope.findFirstOrThrow({
|
||||
where: { id: pendingDocument.id },
|
||||
include: {
|
||||
envelopeItems: true,
|
||||
auditLogs: {
|
||||
orderBy: { createdAt: 'desc' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(updatedEnvelope.title).toBe('Updated Pending Title');
|
||||
expect(updatedEnvelope.envelopeItems[0].title).toBe('Updated Item Title');
|
||||
|
||||
// Verify audit logs were created for both title changes.
|
||||
const titleAuditLog = updatedEnvelope.auditLogs.find(
|
||||
(log) => log.type === 'DOCUMENT_TITLE_UPDATED',
|
||||
);
|
||||
expect(titleAuditLog).toBeDefined();
|
||||
|
||||
const itemAuditLog = updatedEnvelope.auditLogs.find(
|
||||
(log) => log.type === 'ENVELOPE_ITEM_UPDATED',
|
||||
);
|
||||
expect(itemAuditLog).toBeDefined();
|
||||
|
||||
// Verify the upload dropzone is still disabled for pending envelopes.
|
||||
const dropzoneMessage = page.getByText('Cannot upload items after the document has been sent');
|
||||
await expect(dropzoneMessage).toBeVisible();
|
||||
|
||||
// Drag handles are still rendered but dragging is disabled via isDragDisabled
|
||||
// when canItemsBeModified is false. Verify at least that the handle is present
|
||||
// but we cannot programmatically assert isDragDisabled from the DOM.
|
||||
await expect(getEnvelopeItemDragHandles(page)).toHaveCount(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user