@zaunt/zest 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +590 -0
- package/dist/index.d.mts +187 -0
- package/dist/index.mjs +118 -0
- package/dist/quick-reference-BHuN8iCv.mjs +419 -0
- package/package.json +51 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { t as quick_reference_default } from "./quick-reference-BHuN8iCv.mjs";
|
|
2
|
+
//#region src/core/internal/parse-result.d.ts
|
|
3
|
+
type ParseResult<T> = {
|
|
4
|
+
ok: true;
|
|
5
|
+
value: T;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/core/codec.d.ts
|
|
12
|
+
interface Codec<T> {
|
|
13
|
+
parse(text: string): ParseResult<T>;
|
|
14
|
+
format(value: T): string;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/core/fact-state.d.ts
|
|
18
|
+
declare enum FactState {
|
|
19
|
+
NOT_USED = "NOT_USED",
|
|
20
|
+
NOT_CHECKED = "NOT_CHECKED",
|
|
21
|
+
SUCCESS = "SUCCESS",
|
|
22
|
+
FAILURE = "FAILURE"
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/core/fact.d.ts
|
|
26
|
+
type FactMetadata = {
|
|
27
|
+
language?: string;
|
|
28
|
+
} & Record<string, string>;
|
|
29
|
+
declare class Fact {
|
|
30
|
+
private readonly _raw;
|
|
31
|
+
private readonly _metadata;
|
|
32
|
+
readonly kind = "fact";
|
|
33
|
+
private _state;
|
|
34
|
+
private _actualText;
|
|
35
|
+
constructor(_raw: string, _metadata?: FactMetadata);
|
|
36
|
+
get raw(): string;
|
|
37
|
+
get metadata(): FactMetadata;
|
|
38
|
+
get state(): FactState;
|
|
39
|
+
get actualText(): string | undefined;
|
|
40
|
+
private markChecked;
|
|
41
|
+
private assertNotYetChecked;
|
|
42
|
+
asString(): string;
|
|
43
|
+
asInt(): number;
|
|
44
|
+
as<T>(codec: Codec<T>): T;
|
|
45
|
+
assertEquals(actual: string): void;
|
|
46
|
+
assertEquals<T>(actual: T, codec: Codec<T>): void;
|
|
47
|
+
assertEqualsNoThrow(actual: string): void;
|
|
48
|
+
assertEqualsNoThrow<T>(actual: T, codec: Codec<T>): void;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/core/fact-list.d.ts
|
|
52
|
+
type FactDebugInfo = {
|
|
53
|
+
index: number;
|
|
54
|
+
raw: string;
|
|
55
|
+
};
|
|
56
|
+
declare class FactList {
|
|
57
|
+
private readonly _items;
|
|
58
|
+
[index: number]: Fact;
|
|
59
|
+
constructor(items: Fact[]);
|
|
60
|
+
get length(): number;
|
|
61
|
+
[Symbol.iterator](): Iterator<Fact>;
|
|
62
|
+
at(index: number): Fact;
|
|
63
|
+
map<T>(callback: (fact: Fact, index: number) => T): T[];
|
|
64
|
+
byLanguage(language: string): Fact[];
|
|
65
|
+
byLanguage(language: string, n: number): Fact;
|
|
66
|
+
debug(logToConsole?: boolean): FactDebugInfo[];
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/core/row.d.ts
|
|
70
|
+
declare class Row {
|
|
71
|
+
private readonly _headers;
|
|
72
|
+
private readonly _facts;
|
|
73
|
+
constructor(_headers: string[], _facts: Fact[]);
|
|
74
|
+
fact(name: string): Fact;
|
|
75
|
+
value(name: string): string;
|
|
76
|
+
value<T>(name: string, codec: Codec<T>): T;
|
|
77
|
+
toObject(): Record<string, string>;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/core/table.d.ts
|
|
81
|
+
type TableEachRowOptions = {
|
|
82
|
+
expectedColumn?: string;
|
|
83
|
+
codecs?: Record<string, Codec<unknown>>;
|
|
84
|
+
result?: Codec<unknown>;
|
|
85
|
+
failFast?: boolean;
|
|
86
|
+
execute: (...facts: Fact[]) => unknown;
|
|
87
|
+
};
|
|
88
|
+
declare class Table implements Iterable<Row> {
|
|
89
|
+
private readonly _headers;
|
|
90
|
+
private readonly _rows;
|
|
91
|
+
readonly kind = "table";
|
|
92
|
+
private readonly _headerFacts;
|
|
93
|
+
private readonly _cellFacts;
|
|
94
|
+
constructor(_headers: string[], _rows: string[][]);
|
|
95
|
+
get headers(): readonly string[];
|
|
96
|
+
get columnCount(): number;
|
|
97
|
+
get rowCount(): number;
|
|
98
|
+
[Symbol.iterator](): Iterator<Row>;
|
|
99
|
+
row(index: number): Row;
|
|
100
|
+
cell(col: number, row: number): Fact;
|
|
101
|
+
eachRow(executeOrOptions: ((...facts: Fact[]) => unknown) | TableEachRowOptions): Promise<void>;
|
|
102
|
+
toMap(): Record<string, string>;
|
|
103
|
+
toMap(codecs: Record<string, Codec<unknown>>): Record<string, unknown>;
|
|
104
|
+
toRecords(): Record<string, string>[];
|
|
105
|
+
toRecords(codecs: Record<string, Codec<unknown>>): Record<string, unknown>[];
|
|
106
|
+
get state(): FactState;
|
|
107
|
+
/** @internal */
|
|
108
|
+
get headerFacts(): readonly Fact[];
|
|
109
|
+
/** @internal */
|
|
110
|
+
get cellFacts(): readonly (readonly Fact[])[];
|
|
111
|
+
private _createRow;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/core/exhibit.d.ts
|
|
115
|
+
type Exhibit = Fact | Table;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/core/context.d.ts
|
|
118
|
+
type SectionHeading = {
|
|
119
|
+
level: number;
|
|
120
|
+
text: string;
|
|
121
|
+
exhibitStartIndex: number;
|
|
122
|
+
exhibitEndIndex: number;
|
|
123
|
+
};
|
|
124
|
+
declare class Context {
|
|
125
|
+
readonly facts: FactList;
|
|
126
|
+
readonly tables: Table[];
|
|
127
|
+
readonly exhibits: Exhibit[];
|
|
128
|
+
private readonly _allExhibits;
|
|
129
|
+
private readonly _headings;
|
|
130
|
+
private readonly _scopeStart;
|
|
131
|
+
private readonly _scopeEnd;
|
|
132
|
+
constructor(allExhibits: Exhibit[], headings: SectionHeading[], scopeStart: number, scopeEnd: number);
|
|
133
|
+
get sectionNames(): string[];
|
|
134
|
+
get allSectionNames(): {
|
|
135
|
+
name: string;
|
|
136
|
+
level: number;
|
|
137
|
+
}[];
|
|
138
|
+
section(name: string | RegExp): Context;
|
|
139
|
+
debug(logToConsole?: boolean): FactDebugInfo[];
|
|
140
|
+
private _scopedHeadings;
|
|
141
|
+
private _directChildHeadings;
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/integration/scenario-definitions.d.ts
|
|
145
|
+
type ExecuteArgs = Context;
|
|
146
|
+
type ScenarioDefinition = {
|
|
147
|
+
name?: string;
|
|
148
|
+
markdown: string;
|
|
149
|
+
result?: Codec<unknown>;
|
|
150
|
+
failFast?: boolean;
|
|
151
|
+
debug?: boolean;
|
|
152
|
+
execute: (args: ExecuteArgs) => unknown;
|
|
153
|
+
};
|
|
154
|
+
type EachRowDefinition = {
|
|
155
|
+
name?: string;
|
|
156
|
+
markdown: string;
|
|
157
|
+
codecs?: Record<string, Codec<unknown>>;
|
|
158
|
+
expectedColumn?: string;
|
|
159
|
+
result?: Codec<unknown>;
|
|
160
|
+
failFast?: boolean;
|
|
161
|
+
execute: (...facts: Fact[]) => unknown;
|
|
162
|
+
};
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/integration/scenario-api.d.ts
|
|
165
|
+
type ScenarioFn = (definition: ScenarioDefinition) => void;
|
|
166
|
+
type EachRowFn = (definition: EachRowDefinition) => void;
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/core/codecs.d.ts
|
|
169
|
+
declare const stringCodec: Codec<string>;
|
|
170
|
+
declare const intCodec: Codec<number>;
|
|
171
|
+
declare const floatCodec: (fractionDigits?: number) => Codec<number>;
|
|
172
|
+
declare const booleanCodec: (truthy?: string, falsy?: string, ignoreCase?: boolean) => Codec<boolean>;
|
|
173
|
+
declare const enumCodec: <T extends string>(values: readonly T[], ignoreCase?: boolean) => Codec<T>;
|
|
174
|
+
declare const mappedCodec: <T>(parseMap: Record<string, T>, formatMap: Map<T, string>, ignoreCase?: boolean) => Codec<T>;
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/config/zest-config.d.ts
|
|
177
|
+
type ZestConfig = {
|
|
178
|
+
outputEnabled: boolean;
|
|
179
|
+
outputDir: string;
|
|
180
|
+
failFast: boolean;
|
|
181
|
+
};
|
|
182
|
+
declare const setConfig: (overrides: Partial<ZestConfig>) => void;
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/integration/vitest/vitest-scenarios.d.ts
|
|
185
|
+
declare const scenario: ScenarioFn, eachRow: EachRowFn;
|
|
186
|
+
//#endregion
|
|
187
|
+
export { Context, type ExecuteArgs, type Exhibit, Fact, FactList, Row, Table, type ZestConfig, booleanCodec, eachRow, enumCodec, floatCodec, intCodec, mappedCodec, quick_reference_default as quickReference, scenario, setConfig, stringCodec };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import{t as e}from"./quick-reference-BHuN8iCv.mjs";import t from"markdown-it";import{createHash as n}from"node:crypto";import{mkdirSync as r,writeFileSync as i}from"node:fs";import{join as a}from"node:path";import{test as o}from"vitest";const s={parse(e){return{ok:!0,value:e}},format(e){return e}},c={parse(e){let t=e.trim();if(!/^-?\d+$/.test(t))return{ok:!1,message:`Expected integer but got '${e}'`};let n=Number(t);return Number.isSafeInteger(n)?{ok:!0,value:n}:{ok:!1,message:`Expected safe integer but got '${e}'`}},format(e){return String(e)}},l=e=>({parse(e){let t=e.trim();if(t===``)return{ok:!1,message:`Expected number but got '${e}'`};let n=Number(t);return Number.isFinite(n)?{ok:!0,value:n}:{ok:!1,message:`Expected number but got '${e}'`}},format(t){return e===void 0?String(t):t.toFixed(e)}}),u=(e=`yes`,t=`no`,n=!0)=>({parse(r){let i=n?r.toLowerCase():r,a=n?e.toLowerCase():e,o=n?t.toLowerCase():t;return i===a?{ok:!0,value:!0}:i===o?{ok:!0,value:!1}:{ok:!1,message:`Expected '${e}' or '${t}' but got '${r}'`}},format(n){return n?e:t}}),d=(e,t=!0)=>({parse(n){let r=e.find(e=>t?e.toLowerCase()===n.toLowerCase():e===n);return r?{ok:!0,value:r}:{ok:!1,message:`Expected one of [${e.join(`, `)}] but got '${n}'`}},format(e){return e}}),f=(e,t,n=!0)=>({parse(t){let r=n?t.toLowerCase():t,i=Object.entries(e).map(([e,t])=>[n?e.toLowerCase():e,t]).find(([e])=>e===r);return i?{ok:!0,value:i[1]}:{ok:!1,message:`Expected one of [${Object.keys(e).join(`, `)}] but got '${t}'`}},format(e){let n=t.get(e);if(n===void 0)throw Error(`No format mapping found for value '${String(e)}'`);return n}});var p=class extends Error{expected;actual;constructor(e,t){super(`Expected '${e}' but got '${t}'`),this.expected=e,this.actual=t,this.name=`EqualityError`}};const m=e=>{let t=e.split(`
|
|
2
|
+
`),n=0;for(;n<t.length&&t[n].trim()===``;)n++;let r=t.length-1;for(;r>n&&t[r].trim()===``;)r--;return t.slice(n,r+1).join(`
|
|
3
|
+
`)};var h=class{_raw;_metadata;kind=`fact`;_state=`NOT_USED`;_actualText;constructor(e,t={}){this._raw=e,this._metadata=t,this._raw=m(e)}get raw(){return this._raw}get metadata(){return this._metadata}get state(){return this._state}get actualText(){return this._actualText}markChecked(){this._state===`NOT_USED`&&(this._state=`NOT_CHECKED`)}assertNotYetChecked(){if(this._state===`SUCCESS`||this._state===`FAILURE`)throw Error(`The fact has already been checked.`)}asString(){return this.markChecked(),this._raw}asInt(){return this.as(c)}as(e){this.markChecked();let t=e.parse(this._raw);if(!t.ok)throw Error(t.message);return t.value}assertEquals(e,t){this.assertNotYetChecked();let n=t??s,r=this.as(n),i=n.format(r),a=n.format(e);if(this._actualText=a,a===i)this._state=`SUCCESS`;else throw this._state=`FAILURE`,new p(i,a)}assertEqualsNoThrow(e,t){this.assertNotYetChecked();let n=t??s,r=this.as(n),i=n.format(r),a=n.format(e);this._actualText=a,this._state=a===i?`SUCCESS`:`FAILURE`}},g=class{_headers;_facts;constructor(e,t){this._headers=e,this._facts=t}fact(e){let t=this._headers.indexOf(e);if(t<0)throw Error(`Unknown column '${e}'.`);return this._facts[t]}value(e,t){let n=this.fact(e);return t?n.as(t):n.asString()}toObject(){let e={};for(let t=0;t<this._headers.length;t++)e[this._headers[t]]=this._facts[t].asString();return e}};const _=()=>`${typeof process<`u`?process.env.TMPDIR??process.env.TEMP??`/tmp`:`/tmp`}/zest-output`,v=(e,t)=>{if(typeof process>`u`)return t;let n=process.env[e];return n===void 0?t:n.toLowerCase()!==`false`&&n!==`0`},y=(e,t)=>typeof process>`u`?t:process.env[e]??t,b=e=>({outputEnabled:e?.outputEnabled??v(`ZEST_OUTPUT_ENABLED`,!0),outputDir:e?.outputDir??y(`ZEST_OUTPUT_DIR`,_()),failFast:e?.failFast??v(`ZEST_FAIL_FAST`,!1),...e});let x=b();const S=()=>x,C=e=>{x=b(e)},w=e=>e===void 0?S().failFast:e,T=async(e,t)=>{let n=w(t.failFast),r=t.result??s,i=t.expectedColumn??e.headers[e.headers.length-1],a=t.codecs??{},o=!1,c=[];for(let s=0;s<e.rowCount&&!o;s++){let l=e.row(s),u=[];for(let t of e.headers)u.push(l.fact(t));try{let e=await t.execute(...u);if(e!==void 0){let t=l.fact(i),o=a[i]??r;n?t.assertEquals(e,o):t.assertEqualsNoThrow(e,o)}}catch(e){(e instanceof p||e instanceof Error)&&(c.push(e),n&&(o=!0))}}if(c.length===1)throw c[0];if(c.length>1){let e=c[0],t=`Found ${c.length} failures. First: ${e.message}`,n=Error(t);throw e instanceof p&&Object.assign(n,{expected:e.expected,actual:e.actual}),n}};var E=class{_headers;_rows;kind=`table`;_headerFacts;_cellFacts;constructor(e,t){this._headers=e,this._rows=t,this._headerFacts=e.map(e=>new h(e)),this._cellFacts=t.map(e=>e.map(e=>new h(e)))}get headers(){return this._headers}get columnCount(){return this._headers.length}get rowCount(){return this._rows.length}[Symbol.iterator](){let e=0;return{next:()=>{if(e>=this._rows.length)return{done:!0,value:void 0};let t=this._createRow(e);return e+=1,{done:!1,value:t}}}}row(e){if(e<0||e>=this._rows.length)throw Error(`Row ${e} does not exist.`);return this._createRow(e)}cell(e,t){if(e<0||e>=this._headers.length)throw Error(`Column ${e} does not exist.`);if(t===0)return this._headerFacts[e];let n=t-1;if(n<0||n>=this._rows.length)throw Error(`Row ${t} does not exist.`);return this._cellFacts[n][e]}eachRow(e){let t=typeof e==`function`?{execute:e}:e;return T(this,t)}toMap(e){let t=/* @__PURE__ */ new Set;if(this._headers.length===2&&!e){let e={};for(let n of this._rows){let r=n[0];if(t.has(r))throw Error(`Duplicate key '${r}'.`);t.add(r),e[r]=n[1]}return e}if(this._headers.length===2&&e){let n=e[this._headers[1]],r={};for(let e of this._rows){let i=e[0];if(t.has(i))throw Error(`Duplicate key '${i}'.`);t.add(i);let a=e[1];if(n){let e=n.parse(a);if(!e.ok)throw Error(e.message);r[i]=e.value}else r[i]=a}return r}let n={};for(let r=0;r<this._rows.length;r++){let i=this._rows[r],a=i[0];if(t.has(a))throw Error(`Duplicate key '${a}'.`);t.add(a);let o={};for(let t=1;t<this._headers.length;t++){let n=this._headers[t],r=i[t]??``,a=e?.[n];if(a){let e=a.parse(r);if(!e.ok)throw Error(e.message);o[n]=e.value}else o[n]=r}n[a]=o}return n}toRecords(e){let t=[];for(let n of this._rows){let r={};for(let t=0;t<this._headers.length;t++){let i=this._headers[t],a=n[t]??``,o=e?.[i];if(o){let e=o.parse(a);if(!e.ok)throw Error(e.message);r[i]=e.value}else r[i]=a}t.push(r)}return t}get state(){let e=!1,t=!1,n=!1;for(let r of this._headerFacts){let i=r.state;i===`FAILURE`&&(e=!0),i===`NOT_CHECKED`&&(t=!0),i===`SUCCESS`&&(n=!0)}for(let r of this._cellFacts)for(let i of r){let r=i.state;r===`FAILURE`&&(e=!0),r===`NOT_CHECKED`&&(t=!0),r===`SUCCESS`&&(n=!0)}return e?`FAILURE`:n?`SUCCESS`:t?`NOT_CHECKED`:`NOT_USED`}get headerFacts(){return this._headerFacts}get cellFacts(){return this._cellFacts}_createRow(e){return new g(this._headers,this._cellFacts[e])}};const D=new t({html:!0}),O=/* @__PURE__ */ new Set([`div`,`section`,`aside`,`article`,`p`,`prose`]),k=e=>{let t=e.match(/\bclass\s*=\s*["']([^"']*)["']/i);return t?t[1].trim().split(/\s+/).includes(`prose`):!1},A=/<(\/)?([a-zA-Z0-9-]+)([^>]*)>/g,j=(e,t)=>{A.lastIndex=0;let n;for(;(n=A.exec(e))!=null;){let e=!!n[1],r=n[2].toLowerCase(),i=n[3];i.trimEnd().endsWith(`/`)||(t.proseTag===void 0?!e&&O.has(r)&&(r===`prose`||k(i))&&(t.proseTag=r,t.depth=1):r===t.proseTag&&(e?(--t.depth,t.depth<=0&&(t.proseTag=void 0,t.depth=0)):t.depth+=1))}},M=(e,t)=>{let n=[],r=[],i=t+1;for(;i<e.length&&e[i].type!==`thead_close`;)e[i].type===`th_open`&&(i++,i<e.length&&e[i].type===`inline`&&n.push(e[i].content.trim())),i++;for(i++;i<e.length&&e[i].type!==`tbody_close`;){if(e[i].type===`tr_open`){let t=[];for(i++;i<e.length&&e[i].type!==`tr_close`;)e[i].type===`td_open`&&(i++,i<e.length&&e[i].type===`inline`&&t.push(e[i].content.trim())),i++;r.push(t)}i++}for(i++;i<e.length&&e[i].type!==`table_close`;)i++;return{table:new E(n,r),endIndex:i}},N=/* @__PURE__ */ new Set([`code_inline`,`em_open`,`strong_open`]),P=(e,t)=>{let n=e.children??[],r=!1,i=[],a;for(let e=0;e<n.length;e++){let o=n[e];if(o.type===`code_inline`){if(r)throw Error(`Nested facts aren't supported yet.`);let e=new h(o.content),n=o.meta??{};n.fact=e,o.meta=n,t.push({kind:`fact`,fact:e,token:o});continue}if(o.type===`em_open`||o.type===`strong_open`){if(r)throw Error(`Nested facts aren't supported yet.`);r=!0,i=[],a=o;continue}if(o.type===`em_close`||o.type===`strong_close`){if(r){let e=new h(i.join(``)),n=a.meta??{};n.fact=e,a.meta=n;let s=o.meta??{};s.fact=e,o.meta=s,t.push({kind:`fact`,fact:e,token:a}),r=!1,i=[],a=void 0}continue}if(r){if(N.has(o.type))throw Error(`Nested facts aren't supported yet.`);o.type===`text`?i.push(o.content):o.type===`softbreak`&&i.push(`
|
|
4
|
+
`)}}},F=e=>{if(e.type===`heading_open`)return{level:parseInt(e.tag.slice(1),10)}},I=(e,t)=>{let n=[];for(let r=0;r<e.length;r++){let i=e[r],a=t;for(let t=r+1;t<e.length;t++)if(e[t].level<=i.level){a=e[t].exhibitStartIndex;break}n.push({level:i.level,text:i.text,exhibitStartIndex:i.exhibitStartIndex,exhibitEndIndex:a})}return n},L=e=>{let t=e.replace(/\r\n/g,`
|
|
5
|
+
`),n=D.parse(t,{}),r=[],i=[],a={proseTag:void 0,depth:0},o;for(let e=0;e<n.length;e++){let t=n[e];if(t.type===`html_block`){j(t.content,a);continue}let s=a.proseTag!==void 0;if(t.type===`bullet_list_open`||t.type===`ordered_list_open`){if(!s)throw Error(`Zest doesn't support lists yet.`);continue}let c=F(t);if(c){s||(o=c.level);continue}if(o!==void 0&&t.type===`inline`){i.push({level:o,text:t.content.trim(),exhibitStartIndex:r.length}),o=void 0;continue}if(t.type===`heading_close`){o=void 0;continue}if(t.type===`table_open`){let{table:i,endIndex:a}=M(n,e);if(!s){let e=t.meta??{};e.table=i,t.meta=e,r.push({kind:`table`,table:i,token:t})}e=a;continue}if(t.type===`fence`||t.type===`code_block`){if(s)continue;let e={};t.type===`fence`&&t.info&&t.info.trim()!==``&&(e.language=t.info.trim());let n=new h(t.content,e),i=t.meta??{};i.fact=n,t.meta=i,r.push({kind:`fact`,fact:n,token:t});continue}if(t.type===`inline`){if(t.children)for(let e of t.children)e.type===`html_inline`&&j(e.content,a);!s&&a.proseTag===void 0&&P(t,r)}}let s=I(i,r.length);return{exhibits:r.map(e=>e.kind===`fact`?e.fact:e.table),facts:r.filter(e=>e.kind===`fact`).map(e=>e.fact),tables:r.filter(e=>e.kind===`table`).map(e=>e.table),parsedExhibits:r,tokens:n,markdown:t,headings:s}},R=e=>{if(typeof e!=`string`)throw TypeError(`Expected a string, got ${typeof e}. Maybe you need a codec?`);return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)},z=e=>{let t=R(e.raw);return e.state===`FAILURE`&&e.actualText!==void 0?`<del>${t}</del><ins>${R(e.actualText)}</ins>`:t},B=e=>{let t=[];t.push(`<table>`),t.push(`<thead>`),t.push(`<tr>`);for(let n of e.headerFacts)t.push(`<th class="${n.state}">${z(n)}</th>`);t.push(`</tr>`),t.push(`</thead>`),t.push(`<tbody>`);for(let n of e.cellFacts){t.push(`<tr>`);for(let e of n)t.push(`<td class="${e.state}">${z(e)}</td>`);t.push(`</tr>`)}return t.push(`</tbody>`),t.push(`</table>`),t.join(`
|
|
6
|
+
`)};function V(e){return typeof e==`object`&&!!e&&`fact`in e}const H=e=>{let t=/* @__PURE__ */ new Set;for(let n of e){if(n.type!==`inline`)continue;let e=n.children??[],r=!1;for(let n of e){if((n.type===`em_open`||n.type===`strong_open`)&&V(n.meta)&&n.meta.fact){r=!0;continue}if((n.type===`em_close`||n.type===`strong_close`)&&V(n.meta)&&n.meta.fact){r=!1;continue}r&&t.add(n)}}return t},U=(e,n,r,i)=>{let a=new t,o=H(r),s=i.filter(e=>e.kind===`table`),c=0,l=!1;a.renderer.rules.text=(e,t)=>l||o.has(e[t])?``:R(e[t].content),a.renderer.rules.softbreak=(e,t)=>l||o.has(e[t])?``:`
|
|
7
|
+
`,a.renderer.rules.code_inline=(e,t)=>{if(l)return``;let n=e[t],r=V(n.meta)?n.meta.fact:void 0;return r?`<code class="${r.state}">${z(r)}</code>`:`<code>${R(n.content)}</code>`},a.renderer.rules.em_open=(e,t,n,r,i)=>{if(l)return``;let a=e[t],o=V(a.meta)?a.meta.fact:void 0;return o?`<em class="${o.state}">${z(o)}`:`<em`+i.renderAttrs(a)+`>`},a.renderer.rules.em_close=()=>l?``:`</em>`,a.renderer.rules.strong_open=(e,t,n,r,i)=>{if(l)return``;let a=e[t],o=V(a.meta)?a.meta.fact:void 0;return o?`<strong class="${o.state}">${z(o)}`:`<strong`+i.renderAttrs(a)+`>`},a.renderer.rules.strong_close=()=>l?``:`</strong>`,a.renderer.rules.fence=(e,t)=>{let n=e[t],r=V(n.meta)?n.meta.fact:void 0;return r?`<pre><code class="${r.state}">${z(r)}</code></pre>\n`:`<pre><code${n.info&&n.info.trim()?` class="language-${R(n.info.trim())}"`:``}>${R(n.content)}</code></pre>\n`},a.renderer.rules.code_block=(e,t)=>{let n=e[t],r=V(n.meta)?n.meta.fact:void 0;return r?`<pre><code class="${r.state}">${z(r)}</code></pre>\n`:`<pre><code>${R(n.content)}</code></pre>\n`},a.renderer.rules.table_open=()=>{let e=s[c];return e?(l=!0,B(e.table)):`<table>`},a.renderer.rules.table_close=()=>{let e=l;return l=!1,e?(c++,``):`</table>`};let u=(e,t,n,r,i)=>l?``:i.renderToken(e,t,n);a.renderer.rules.thead_open=u,a.renderer.rules.thead_close=u,a.renderer.rules.tbody_open=u,a.renderer.rules.tbody_close=u,a.renderer.rules.tr_open=u,a.renderer.rules.tr_close=u,a.renderer.rules.th_open=u,a.renderer.rules.th_close=u,a.renderer.rules.td_open=u,a.renderer.rules.td_close=u;let d=a.renderer.render(r,a.options,{});return`<!DOCTYPE html>
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="UTF-8" />
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
12
|
+
<title>${R(e)}</title>
|
|
13
|
+
<style>
|
|
14
|
+
html {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
font-family: Arial;
|
|
17
|
+
font-size: 17px;
|
|
18
|
+
}
|
|
19
|
+
*, *:before, *:after {
|
|
20
|
+
box-sizing: inherit;
|
|
21
|
+
}
|
|
22
|
+
body {
|
|
23
|
+
padding: 24px;
|
|
24
|
+
overflow-wrap: break-word;
|
|
25
|
+
}
|
|
26
|
+
.content {
|
|
27
|
+
margin: 0 auto;
|
|
28
|
+
max-width: 800px;
|
|
29
|
+
}
|
|
30
|
+
table {
|
|
31
|
+
border-collapse: collapse;
|
|
32
|
+
border-spacing: 0;
|
|
33
|
+
box-shadow: 3px 5px 7px 0px rgba(0,0,0,0.1);
|
|
34
|
+
margin: 20px 0;
|
|
35
|
+
}
|
|
36
|
+
th {
|
|
37
|
+
font-weight: bold;
|
|
38
|
+
background-color: #e3e3e3;
|
|
39
|
+
vertical-align: top;
|
|
40
|
+
}
|
|
41
|
+
td {
|
|
42
|
+
background-color: #fff;
|
|
43
|
+
}
|
|
44
|
+
td, th {
|
|
45
|
+
border: 1px solid #333;
|
|
46
|
+
padding: 4px 6px;
|
|
47
|
+
}
|
|
48
|
+
pre, code {
|
|
49
|
+
font-family: Lucida Sans Typewriter, monospace;
|
|
50
|
+
font-size: 0.9rem;
|
|
51
|
+
line-height: 1.4rem;
|
|
52
|
+
}
|
|
53
|
+
pre {
|
|
54
|
+
padding: 10px 16px !important;
|
|
55
|
+
background-color: #f3f3f3;
|
|
56
|
+
}
|
|
57
|
+
p {
|
|
58
|
+
line-height: 1.5rem;
|
|
59
|
+
}
|
|
60
|
+
.prose, prose {
|
|
61
|
+
margin: 16px 0;
|
|
62
|
+
}
|
|
63
|
+
.prose ul, .prose ol, prose ul, prose ol {
|
|
64
|
+
padding-left: 24px;
|
|
65
|
+
margin: 12px 0;
|
|
66
|
+
}
|
|
67
|
+
.prose li, prose li {
|
|
68
|
+
line-height: 1.5rem;
|
|
69
|
+
margin-bottom: 4px;
|
|
70
|
+
}
|
|
71
|
+
.SUCCESS, .FAILURE {
|
|
72
|
+
padding: 2px 5px;
|
|
73
|
+
border-radius: 5px;
|
|
74
|
+
}
|
|
75
|
+
table .SUCCESS, table .FAILURE {
|
|
76
|
+
border-radius: 0;
|
|
77
|
+
}
|
|
78
|
+
.SUCCESS {
|
|
79
|
+
background-color: rgba(0,255,20, 0.3);
|
|
80
|
+
}
|
|
81
|
+
.FAILURE {
|
|
82
|
+
background-color: rgba(255, 64, 40, 0.3);
|
|
83
|
+
}
|
|
84
|
+
.FAILURE ins {
|
|
85
|
+
background-color: #def3ff;
|
|
86
|
+
font-weight: bold;
|
|
87
|
+
text-decoration: none;
|
|
88
|
+
padding: 2px 1px;
|
|
89
|
+
margin: 0 -1px;
|
|
90
|
+
}
|
|
91
|
+
.FAILURE del {
|
|
92
|
+
background-color: #f36b6b;
|
|
93
|
+
font-weight: bold;
|
|
94
|
+
text-decoration: line-through;
|
|
95
|
+
text-decoration-thickness: 2px;
|
|
96
|
+
padding: 2px 1px;
|
|
97
|
+
margin: 0 -1px;
|
|
98
|
+
}
|
|
99
|
+
pre code.SUCCESS, pre code.FAILURE {
|
|
100
|
+
padding-left: 0;
|
|
101
|
+
padding-right: 0;
|
|
102
|
+
}
|
|
103
|
+
.NOT_CHECKED {
|
|
104
|
+
background-color: #ddf7f6;
|
|
105
|
+
}
|
|
106
|
+
.NOT_USED {
|
|
107
|
+
xbackground-color: #eaeaea;
|
|
108
|
+
color: #777;
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
111
|
+
</head>
|
|
112
|
+
<body>
|
|
113
|
+
<div class="content">
|
|
114
|
+
${d}
|
|
115
|
+
</div>
|
|
116
|
+
</body>
|
|
117
|
+
</html>`},W=(e,t)=>e?e.replace(/[^a-zA-Z0-9_-]/g,`_`).slice(0,80):n(`sha256`).update(t).digest(`hex`).slice(0,12),G=(e,t,n)=>{let o=S();if(!o.outputEnabled)return;let s=o.outputDir;r(s,{recursive:!0});let c=W(e,n)+`.html`,l=a(s,c);return i(l,t,`utf-8`),l},K=(e,t,n)=>{let r=0,i,a=(e,t)=>{r+=1,!i&&e!==void 0&&(i=`Expected '${e}' but got '${t}'`)};for(let e of n)if(e.kind===`fact`)e.fact.state===`FAILURE`&&a(e.fact.raw,e.fact.actualText);else{for(let t of e.table.headerFacts)t.state===`FAILURE`&&a(t.raw,t.actualText);for(let t of e.table.cellFacts)for(let e of t)e.state===`FAILURE`&&a(e.raw,e.actualText)}return{name:e,markdown:t,passed:r===0,failureCount:r,firstFailureMessage:i}};var q=class{_items;constructor(e){return this._items=e,new Proxy(this,{get(e,t,n){return typeof t==`string`&&/^\d+$/.test(t)?e._items[Number(t)]:Reflect.get(e,t,n)}})}get length(){return this._items.length}[Symbol.iterator](){return this._items[Symbol.iterator]()}at(e){let t=e<0?this._items.length+e:e,n=this._items[t];if(n===void 0)throw Error(`No fact at index ${e} (found ${this._items.length})`);return n}map(e){return this._items.map(e)}byLanguage(e,t){let n=this._items.filter(t=>t.metadata.language===e);if(t===void 0)return n;let r=n[t];if(r===void 0)throw Error(`No ${e} block at index ${t} (found ${n.length})`);return r}debug(e=!0){let t=this._items.map((e,t)=>({index:t,raw:e.raw}));if(e)for(let{index:e,raw:n}of t){let t=n.includes(`
|
|
118
|
+
`)?"`":`'`;console.log(`${e}: ${t}${n}${t}`)}return t}},J=class e{facts;tables;exhibits;_allExhibits;_headings;_scopeStart;_scopeEnd;constructor(e,t,n,r){this._allExhibits=e,this._headings=t,this._scopeStart=n,this._scopeEnd=r,this.exhibits=e.slice(n,r),this.facts=new q(this.exhibits.filter(e=>e.kind===`fact`)),this.tables=this.exhibits.filter(e=>e.kind===`table`)}get sectionNames(){return this._directChildHeadings().map(e=>e.text)}get allSectionNames(){return this._scopedHeadings().map(e=>({name:e.text,level:e.level}))}section(t){let n=this._directChildHeadings(),r=typeof t==`string`?n.filter(e=>e.text===t):n.filter(e=>t.test(e.text));if(r.length===0){let e=typeof t==`string`?`"${t}"`:`${t}`;throw Error(`No section found matching ${e}. Available sections: ${n.map(e=>`"${e.text}"`).join(`, `)||`(none)`}`)}if(r.length>1){let e=typeof t==`string`?`"${t}"`:`${t}`;throw Error(`Multiple sections match ${e}: ${r.map(e=>`"${e.text}"`).join(`, `)}`)}let i=r[0];return new e(this._allExhibits,this._headings,i.exhibitStartIndex,i.exhibitEndIndex)}debug(e=!0){return this.facts.debug(e)}_scopedHeadings(){return this._headings.filter(e=>e.level!==1&&e.exhibitStartIndex>=this._scopeStart&&e.exhibitEndIndex<=this._scopeEnd)}_directChildHeadings(){let e=this._scopedHeadings();if(e.length===0)return[];let t=[],n=0;for(let r of e)(n===0||r.level<=n)&&(t.push(r),n=r.level);return t}};const Y=e=>e.replace(/\s+/g,` `).trim().slice(0,120)||`scenario`,X=()=>`each-row scenario`,Z=e=>e===void 0?S().failFast:e,Q=(e,t,n)=>{if(t.length===0)throw Error(`No facts found. Cannot assert a return value without at least one fact.`);t[t.length-1].assertEquals(e,n)},$=(e,t,n)=>{if(t.length===0)throw Error(`No facts found. Cannot assert a return value without at least one fact.`);t[t.length-1].assertEqualsNoThrow(e,n)},{scenario:ee,eachRow:te}=(e=>({scenario:t=>{let n=t.name??Y(t.markdown);e.registerTest({name:n,async run(){let e=L(t.markdown);if(e.exhibits.length===0)throw Error(`Scenario must contain at least one fact or table.`);let r=Z(t.failFast),i=t.result??s,a=new J(e.exhibits,e.headings,0,e.exhibits.length);if(t.debug&&a.debug(!0),r)try{let n=await t.execute(a);n!==void 0&&Q(n,e.facts,i)}finally{let r=U(n,t.markdown,e.tokens,e.parsedExhibits);G(n,r,t.markdown)}else{let r;try{let n=await t.execute(a);n!==void 0&&$(n,e.facts,i)}catch(e){e instanceof p||(r=e)}let o=K(n,t.markdown,e.parsedExhibits),s=U(n,t.markdown,e.tokens,e.parsedExhibits);if(G(n,s,t.markdown),r)throw r;if(!o.passed){let e=o.failureCount===1?`Failure: ${o.firstFailureMessage}`:`Found ${o.failureCount} failures. First: ${o.firstFailureMessage}`;throw Error(e)}}}})},eachRow:t=>{let n=t.name??X(),r;try{r=L(t.markdown)}catch(t){e.registerTest({name:n,run(){throw t}});return}let i=r.parsedExhibits.find(e=>e.kind===`table`);if(!i||i.kind!==`table`){e.registerTest({name:n,run(){throw Error(`Expected a table in the scenario.`)}});return}let a=i.table;e.registerTest({name:n,async run(){try{await T(a,{expectedColumn:t.expectedColumn,codecs:t.codecs,result:t.result,failFast:t.failFast,execute:t.execute});let e=K(n,t.markdown,r.parsedExhibits);if(!e.passed){let t=e.failureCount===1?`Failure: ${e.firstFailureMessage}`:`Found ${e.failureCount} failures. First: ${e.firstFailureMessage}`;throw Error(t)}}finally{let e=U(n,t.markdown,r.tokens,r.parsedExhibits);G(n,e,t.markdown)}}})}}))({registerTest(e){o(e.name,e.run)}});export{J as Context,h as Fact,q as FactList,g as Row,E as Table,u as booleanCodec,te as eachRow,d as enumCodec,l as floatCodec,c as intCodec,f as mappedCodec,e as quickReference,ee as scenario,C as setConfig,s as stringCodec};
|