mirror of
https://github.com/documenso/documenso.git
synced 2026-08-27 00:32:29 +10:00
fix: wip
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import { createDocumentFromDirectTemplate } from '@documenso/lib/server-only/template/create-document-from-direct-template';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { seedDirectTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { DocumentSigningOrder, FieldType, RecipientRole } from '@prisma/client';
|
||||
|
||||
/**
|
||||
* "Dictate next signer" lets the signer choose who acts in the NEXT step. With
|
||||
* signing groups the direct recipient can share a step with someone else, and
|
||||
* because the direct recipient is created as SIGNED before the pending query
|
||||
* runs, that same-step peer would otherwise look like the "next" recipient.
|
||||
*
|
||||
* The UI never offers dictation in that case, so this exercises the server
|
||||
* directly — the only way the gap is reachable.
|
||||
*/
|
||||
|
||||
const requestMetadata: ApiRequestMetadata = {
|
||||
requestMetadata: {},
|
||||
source: 'app',
|
||||
auth: null,
|
||||
};
|
||||
|
||||
const PEER_EMAIL = 'peer@documenso.com';
|
||||
const PEER_NAME = 'Peer Signer';
|
||||
const LATER_EMAIL = 'later@documenso.com';
|
||||
const LATER_NAME = 'Later Signer';
|
||||
|
||||
const DICTATED = { email: 'dictated@documenso.com', name: 'Dictated Signer' };
|
||||
|
||||
/**
|
||||
* Seeds a direct template whose direct recipient sits at `directSigningOrder`,
|
||||
* plus a peer at `peerSigningOrder` and a signer in a strictly later step.
|
||||
*/
|
||||
const seedDirectTemplateWithPeer = async (options: { peerSigningOrder: number }) => {
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
const template = await seedDirectTemplate({
|
||||
title: '[TEST] Direct template dictation',
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
await prisma.documentMeta.update({
|
||||
where: { id: template.documentMetaId },
|
||||
data: {
|
||||
signingOrder: DocumentSigningOrder.SEQUENTIAL,
|
||||
allowDictateNextSigner: true,
|
||||
},
|
||||
});
|
||||
|
||||
const envelopeItem = await prisma.envelopeItem.findFirstOrThrow({
|
||||
where: { envelopeId: template.id },
|
||||
});
|
||||
|
||||
// Every SIGNER needs a signature field or the direct-template flow rejects
|
||||
// the template before it reaches the dictation logic.
|
||||
const createSigner = async (email: string, name: string, signingOrder: number) => {
|
||||
const recipient = await prisma.recipient.create({
|
||||
data: {
|
||||
envelopeId: template.id,
|
||||
email,
|
||||
name,
|
||||
token: Math.random().toString().slice(2, 12),
|
||||
role: RecipientRole.SIGNER,
|
||||
signingOrder,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.field.create({
|
||||
data: {
|
||||
envelopeId: template.id,
|
||||
envelopeItemId: envelopeItem.id,
|
||||
recipientId: recipient.id,
|
||||
type: FieldType.SIGNATURE,
|
||||
page: 1,
|
||||
positionX: 5,
|
||||
positionY: 20 + signingOrder * 5,
|
||||
width: 20,
|
||||
height: 5,
|
||||
customText: '',
|
||||
inserted: false,
|
||||
},
|
||||
});
|
||||
|
||||
return recipient;
|
||||
};
|
||||
|
||||
const peer = await createSigner(PEER_EMAIL, PEER_NAME, options.peerSigningOrder);
|
||||
const later = await createSigner(LATER_EMAIL, LATER_NAME, 2);
|
||||
|
||||
const directRecipient = template.recipients.find((recipient) => recipient.signingOrder === 1);
|
||||
const directSignatureField = template.fields.find((field) => field.type === FieldType.SIGNATURE);
|
||||
|
||||
if (!directRecipient || !directSignatureField) {
|
||||
throw new Error('Seeded direct template is missing its recipient or signature field');
|
||||
}
|
||||
|
||||
// Read updatedAt last: the writes above bump it, and the flow rejects a stale value.
|
||||
const refreshed = await prisma.envelope.findFirstOrThrow({ where: { id: template.id } });
|
||||
|
||||
return {
|
||||
directLinkToken: template.directLink?.token ?? '',
|
||||
directSignatureFieldId: directSignatureField.id,
|
||||
templateUpdatedAt: refreshed.updatedAt,
|
||||
peer,
|
||||
later,
|
||||
};
|
||||
};
|
||||
|
||||
const signDirectTemplate = async (seeded: Awaited<ReturnType<typeof seedDirectTemplateWithPeer>>) =>
|
||||
await createDocumentFromDirectTemplate({
|
||||
directRecipientName: 'Direct Signer',
|
||||
directRecipientEmail: 'direct-signer@documenso.com',
|
||||
directTemplateToken: seeded.directLinkToken,
|
||||
templateUpdatedAt: seeded.templateUpdatedAt,
|
||||
signedFieldValues: [
|
||||
{
|
||||
token: seeded.directLinkToken,
|
||||
fieldId: seeded.directSignatureFieldId,
|
||||
value: 'Direct Signer',
|
||||
isBase64: false,
|
||||
},
|
||||
],
|
||||
nextSigner: DICTATED,
|
||||
requestMetadata,
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATE_DICTATION]: does not dictate a recipient sharing the direct recipient step', async () => {
|
||||
const seeded = await seedDirectTemplateWithPeer({ peerSigningOrder: 1 });
|
||||
|
||||
const { envelopeId } = await signDirectTemplate(seeded);
|
||||
|
||||
const recipients = await prisma.recipient.findMany({ where: { envelopeId } });
|
||||
|
||||
const peer = recipients.find((recipient) => recipient.email === PEER_EMAIL);
|
||||
const dictated = recipients.find((recipient) => recipient.email === DICTATED.email);
|
||||
|
||||
// The same-step peer must be untouched...
|
||||
expect(peer).toBeDefined();
|
||||
expect(peer?.name).toBe(PEER_NAME);
|
||||
expect(peer?.signingOrder).toBe(1);
|
||||
|
||||
// ...and nobody at all should have been renamed, since the next step is not reachable yet.
|
||||
expect(dictated).toBeUndefined();
|
||||
});
|
||||
|
||||
test('[DIRECT_TEMPLATE_DICTATION]: still dictates the next step when the direct recipient is alone', async () => {
|
||||
const seeded = await seedDirectTemplateWithPeer({ peerSigningOrder: 3 });
|
||||
|
||||
const { envelopeId } = await signDirectTemplate(seeded);
|
||||
|
||||
const recipients = await prisma.recipient.findMany({ where: { envelopeId } });
|
||||
|
||||
// The order-2 signer is the sole member of the next step, so dictation applies.
|
||||
const dictated = recipients.find((recipient) => recipient.email === DICTATED.email);
|
||||
|
||||
expect(dictated).toBeDefined();
|
||||
expect(dictated?.name).toBe(DICTATED.name);
|
||||
expect(dictated?.signingOrder).toBe(2);
|
||||
|
||||
// The untouched recipients keep their seeded identities.
|
||||
expect(recipients.some((recipient) => recipient.email === LATER_EMAIL)).toBe(false);
|
||||
expect(recipients.some((recipient) => recipient.email === PEER_EMAIL)).toBe(true);
|
||||
});
|
||||
Reference in New Issue
Block a user