How to Test E-Signature and Document Workflows
Short answer
E-sign flows fail on sign-order violations, expired envelopes, witness requirements skipped, and completed PDFs not archived—not on whether a signature pad draws ink. Use DocuSign/HelloSign sandboxes in Arrange, probe envelope_state after each signer, assert PDF hash or clause presence via probe, and link signer_role scenarios to HR and insurance bind flows.
Part of Testing Guides by industry.
Who this is for
Teams shipping HR offers, insurance bind packets, SaaS order forms, or legal tech integrating DocuSign, Dropbox Sign, Adobe Sign, or in-house signature pads with sequential or parallel signers, witnesses, and ID verification.
Why testing e-signatures matters
Invalid signatures void contracts and block deals:
- Sign order errors — Signer 2 completes before Signer 1; envelope legally void or stuck.
- Expired envelopes — User signs after TTL; CRM shows won deal but no executable contract.
- Witness skipped — Required witness role bypassed; real estate and will workflows invalid.
- Tampered PDF — Post-sign modification; audit fails.
- Wrong document version — Terms v3 sent, v2 signed; regulatory mismatch.
Probe envelope_state, signer_status, and archived PDF metadata—canvas stroke UI is insufficient.
Complexity map
| Scenario | Edge case | Why tests break | Approach |
|---|---|---|---|
| Sign order | Signer2 first | Should block | Probe waiting status |
| Parallel sign | All must complete | Partial complete | Probe each signer |
| Envelope expiry | clock past TTL | Should reject | clock + probe expired |
| Decline to sign | Workflow stop | Still advances | Probe declined |
| Witness required | Missing witness | Completed wrongly | Probe roles complete |
| Embedded vs email | Different URLs | Untested path | Both specs or probe |
| ID verification | KBA fail | Should block | Probe verification |
| CC recipients | Not signers | Confusion | Probe recipient roles |
| Template merge | Wrong field values | Bad contract | Probe merged fields |
| Void envelope | After partial sign | Orphan CRM | Probe void + CRM |
| Mobile sign | Touch pad | Desktop only | Mobile project |
| API webhook lag | Status stale | Early assert | poll envelope probe |
DocuSign sandbox Arrange
// POST /api/test/create-envelope
// Returns { envelopeId, signingUrlSigner1, signingUrlSigner2 }
const { envelopeId, signingUrlSigner1 } = await request.post('/api/test/create-envelope', {
data: {
runId,
templateId: 'demo-offer',
signers: [
{ role: 'employee', email: `signer1-${runId}@test.local`, order: 1 },
{ role: 'hr', email: `signer2-${runId}@test.local`, order: 2 },
],
},
}).then(r => r.json());
Use DocuSign developer sandbox credentials in CI secrets—never prod.
Sign order enforcement
await page.goto(signingUrlSigner2); // signer 2 first — wrong order
await expect(page.getByText(/waiting|not your turn/i)).toBeVisible();
const env = await request.get(`/api/test/envelope/${envelopeId}`).then(r => r.json());
expect(env.status).toBe('sent');
expect(env.signers.find(s => s.role === 'hr').status).toBe('waiting');
await page.goto(signingUrlSigner1);
await completeSigningUI(page); // product-specific clicks + adopt signature
await expect.poll(async () =>
(await request.get(`/api/test/envelope/${envelopeId}`)).json()
).toMatchObject({ signers: expect.arrayContaining([expect.objectContaining({ role: 'employee', status: 'completed' })]) });
PDF Assert via probe
await completeBothSigners(/* ... */);
const pdf = await request.get(`/api/test/envelope/${envelopeId}/pdf`).then(r => r.json());
expect(pdf.sha256).toMatch(/^[a-f0-9]{64}$/);
expect(pdf.containsText).toContain('Offer Terms v3');
Parse PDF text in test env only—avoid storing full PDFs in CI artifacts with PII; use synthetic names.
Expiry with clock
await context.clock.install({ time: new Date('2024-06-01T10:00:00Z') });
await createEnvelope(/* expires in 1h */);
await context.clock.runFor(3_600_000 + 1);
await page.goto(signingUrlSigner1);
const env = await request.get(`/api/test/envelope/${envelopeId}`).then(r => r.json());
expect(env.status).toBe('expired');
Requirement slices to cover
signer_role— employee, hr, witness, counterpartyenvelope_state— sent, completed, declined, expired, voided
Link HR offer flows (HR applications) and insurance bind (insurance quotes).
CI checklist
- Sandbox credentials rotated; demo templates only
- Sign-order negative spec
- Completed envelope probe + PDF metadata
- Expiry spec with clock
- Webhook handler tested via poll if async
- Void/decline paths update CRM probe
Anti-patterns
| Anti-pattern | Why it fails | Better approach |
|---|---|---|
| Draw signature only | No envelope state | Probe completed |
| Prod DocuSign in CI | Legal + rate | Sandbox |
| Skip signer2 | Order bugs | Sequential spec |
| Email link E2E only | Flaky inbox | signingUrl route |
| No PDF assert | Wrong doc archived | containsText probe |
| PII PDF artifacts | Leak | Synthetic merge fields |
Example scenario
Situation: HR offer requires employee then HR counter-sign; employee tries forwarding HR link early.
Expected outcome: HR signing blocked until employee completes; envelope remains sent not completed.
Why UI-only automation breaks: HR UI loads but completed PDF missing employee signature page—unenforceable offer.
- Arrange: Sandbox envelope two ordered signers for runId.
- Act: Attempt HR URL before employee signs; then employee signs; then HR.
- Assert: Probe order enforced; final PDF contains both signatures metadata; envelope completed.
TestChimp workflow: Track envelope_state by signer_role; evolve witness-required templates if prod share grows.
Same Arrange/Act/Assert pattern as expired-coupon checkout.
Connect scenarios to your QA workflow
Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).
Related scenarios
- PDF downloads — archived documents
- HR applications — offer letters
- Insurance quotes — bind packets
- Audit compliance — sign events logged
External references
Frequently asked questions
How do I test DocuSign flows in CI?
Create envelope via sandbox API in Arrange, navigate signingUrl in Playwright, complete adopt-signature UI, poll envelope probe to completed, assert PDF metadata—never prod accounts.
How is sign order tested?
Open second signer URL before first completes—expect blocked UI and probe waiting status. Complete in order; probe completed with both signers.
How do witness flows differ?
Seed template requiring witness role; attempt complete without witness—probe should not reach completed; add witness signer and assert completed.
How do expired envelopes behave?
Install clock, create short TTL envelope, advance past expiry, attempt sign—probe expired and signing rejected.
Webhooks vs polling?
Poll test envelope probe in E2E for simplicity; unit test webhook handler separately. expect.poll until status completed.
How do I avoid PII in signed PDF artifacts?
Use synthetic merge fields and redact CI artifacts; assert containsText clauses not full PDF storage.
Which signer_roles need TrueCoverage priority?
Compare prod envelope templates—evolve witness and parallel signer patterns when undertested vs HR/insurance traffic.
Apply these patterns in your repo
Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.