Files
pn-new-crm/tests/unit/services/documenso-signers.test.ts
Matt 221ae5784e chore(autonomous-session): consolidate uncommitted work from prior session
Bundles the prior autonomous-session output that was sitting unstaged:

- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
  never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
  after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
  redirects (ocr to ai, reports to dashboard, invitations to users),
  docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
  flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
  let-reassign), set-state-in-effect disables in CountryFlag and
  UploadForSigning preview-bytes effect, unused 'confirm' destructures in
  interest contract + reservation tabs, unescaped apostrophe in test-template
  card copy
2026-05-23 00:52:59 +02:00

96 lines
3.2 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
DOC_TYPE_LABEL,
extractSigningToken,
nextPendingSigner,
} from '@/lib/services/documenso-signers';
describe('extractSigningToken', () => {
it('pulls the last path segment when it looks token-shaped', () => {
expect(extractSigningToken('https://sig.example.com/sign/vbT8hi3jKQmrFP_LN1WcS')).toBe(
'vbT8hi3jKQmrFP_LN1WcS',
);
});
it('handles trailing slash gracefully', () => {
expect(extractSigningToken('https://sig.example.com/sign/HkrptwS42ZBXdRKj1TyUo/')).toBe(
'HkrptwS42ZBXdRKj1TyUo',
);
});
it('returns null for non-URL input', () => {
expect(extractSigningToken('not a url at all')).toBeNull();
expect(extractSigningToken('')).toBeNull();
expect(extractSigningToken(null)).toBeNull();
expect(extractSigningToken(undefined)).toBeNull();
});
it('rejects too-short tails (defends against generic "sign" / "embed" terminators)', () => {
expect(extractSigningToken('https://example.com/sign')).toBeNull();
expect(extractSigningToken('https://example.com/abc')).toBeNull();
});
it('rejects path tails with disallowed characters', () => {
// Real tokens are URL-safe base64 - no spaces, no punctuation
expect(extractSigningToken('https://example.com/sign/has%20space')).toBeNull();
});
it('handles real v1.32 + v2 token shapes', () => {
// Documenso 1.32 token (21-char URL-safe alphabet from real webhook docs)
expect(extractSigningToken('https://sig.documenso.com/sign/vbT8hi3jKQmrFP_LN1WcS')).toBe(
'vbT8hi3jKQmrFP_LN1WcS',
);
// Mixed-case + underscores + dashes
expect(extractSigningToken('https://app.example.com/sign/Aa_-Zz09Aa_-Zz09')).toBe(
'Aa_-Zz09Aa_-Zz09',
);
});
});
describe('DOC_TYPE_LABEL', () => {
it('maps the three known document types to customer-facing labels', () => {
expect(DOC_TYPE_LABEL.eoi).toBe('Expression of Interest');
expect(DOC_TYPE_LABEL.contract).toBe('Sales Contract');
expect(DOC_TYPE_LABEL.reservation_agreement).toBe('Reservation Agreement');
});
});
describe('nextPendingSigner', () => {
it('picks the lowest signingOrder pending signer', () => {
const signers = [
{ status: 'signed', signingOrder: 1 },
{ status: 'pending', signingOrder: 2 },
{ status: 'pending', signingOrder: 3 },
];
expect(nextPendingSigner(signers)).toEqual({ status: 'pending', signingOrder: 2 });
});
it('returns null when every signer has signed', () => {
expect(
nextPendingSigner([
{ status: 'signed', signingOrder: 1 },
{ status: 'signed', signingOrder: 2 },
]),
).toBeNull();
});
it('treats declined the same as no longer pending', () => {
expect(
nextPendingSigner([
{ status: 'declined', signingOrder: 1 },
{ status: 'signed', signingOrder: 2 },
]),
).toBeNull();
});
it('handles unordered input by signingOrder', () => {
const signers = [
{ status: 'pending', signingOrder: 5 },
{ status: 'signed', signingOrder: 1 },
{ status: 'pending', signingOrder: 2 },
];
expect(nextPendingSigner(signers)).toEqual({ status: 'pending', signingOrder: 2 });
});
});