mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
Extension-App panel + Connect webhook that summarize, extract key terms, risk-flag clauses, and draft memos over DocuSign envelopes, on the shared @hanzo/ai + @hanzo/iam foundation over api.hanzo.ai /v1. - OAuth Authorization Code Grant (account-d/account.docusign.com), server-side client_secret, base_uri discovery via /oauth/userinfo (pure shaping). - eSignature REST v2.1 wrappers (envelopes, documents, combined PDF). - Connect webhook: X-DocuSign-Signature-N HMAC-SHA256 verify over raw body, constant-time, multi-key; envelope-completed auto-summarize pipeline. - PDF text extraction (pdf.js, pure itemsToText), document windowing/truncation, one 'ask' path shared by the four actions, the panel, and the webhook. - 103 vitest tests (config, oauth, api, webhook HMAC, pdf, hanzo, actions, envelope pipeline, panel). tsc clean, esbuild build green. - Registers packages/docusign in pnpm-workspace.yaml (one line).
56 lines
2.1 KiB
TypeScript
56 lines
2.1 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { itemsToText, extractPdfText, type PdfDoc, type TextRun } from '../src/pdf-text';
|
|
|
|
describe('itemsToText', () => {
|
|
it('joins runs with spaces and breaks lines on hasEOL', () => {
|
|
const runs: TextRun[] = [
|
|
{ str: 'This' }, { str: 'Agreement' }, { str: 'is' }, { str: 'made', hasEOL: true },
|
|
{ str: 'between' }, { str: 'the' }, { str: 'parties.', hasEOL: true },
|
|
];
|
|
expect(itemsToText(runs)).toBe('This Agreement is made\nbetween the parties.');
|
|
});
|
|
|
|
it('skips marked-content markers (no str)', () => {
|
|
expect(itemsToText([{ str: 'a' }, {}, { str: 'b' }])).toBe('a b');
|
|
});
|
|
|
|
it('collapses runs of spaces and blank lines', () => {
|
|
const runs: TextRun[] = [{ str: 'a' }, { str: ' ' }, { str: 'b', hasEOL: true }, { hasEOL: true }, { hasEOL: true }, { hasEOL: true }, { str: 'c' }];
|
|
const out = itemsToText(runs);
|
|
expect(out).not.toMatch(/ {2,}/);
|
|
expect(out).not.toMatch(/\n{3,}/);
|
|
expect(out).toContain('a');
|
|
expect(out).toContain('c');
|
|
});
|
|
|
|
it('is total on empty input', () => {
|
|
expect(itemsToText([])).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('extractPdfText (injected loader — no real parser)', () => {
|
|
// A fake pdf.js document: two pages of text runs.
|
|
const fakeDoc: PdfDoc = {
|
|
numPages: 2,
|
|
async getPage(n: number) {
|
|
const map: Record<number, TextRun[]> = {
|
|
1: [{ str: 'Page' }, { str: 'one', hasEOL: true }],
|
|
2: [{ str: 'Page' }, { str: 'two', hasEOL: true }],
|
|
};
|
|
return { async getTextContent() { return { items: map[n] }; } };
|
|
},
|
|
};
|
|
|
|
it('extracts one PageTextItem per page, in order', async () => {
|
|
const pages = await extractPdfText(new ArrayBuffer(8), async () => fakeDoc);
|
|
expect(pages).toHaveLength(2);
|
|
expect(pages[0]).toEqual({ page: 1, text: 'Page one' });
|
|
expect(pages[1]).toEqual({ page: 2, text: 'Page two' });
|
|
});
|
|
|
|
it('handles a zero-page document', async () => {
|
|
const empty: PdfDoc = { numPages: 0, async getPage() { throw new Error('no pages'); } };
|
|
expect(await extractPdfText(new ArrayBuffer(0), async () => empty)).toEqual([]);
|
|
});
|
|
});
|