@rtif-sdk/test-kit 1.0.0 → 1.2.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/dist/assertions.d.ts +36 -2
- package/dist/assertions.d.ts.map +1 -1
- package/dist/assertions.js +137 -6
- package/dist/assertions.js.map +1 -1
- package/dist/builders.d.ts +201 -12
- package/dist/builders.d.ts.map +1 -1
- package/dist/builders.js +272 -19
- package/dist/builders.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +13 -4
package/dist/assertions.d.ts
CHANGED
|
@@ -3,11 +3,45 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Document, Selection } from '@rtif-sdk/core';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Options for `assertDocEqual`.
|
|
7
7
|
*/
|
|
8
|
-
export
|
|
8
|
+
export interface DocEqualOptions {
|
|
9
|
+
/**
|
|
10
|
+
* When true, block IDs must also match.
|
|
11
|
+
* Default: false (block IDs are ignored).
|
|
12
|
+
*/
|
|
13
|
+
compareIds?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Assert that two documents are structurally equal.
|
|
17
|
+
*
|
|
18
|
+
* By default, block IDs are ignored — only block content (type, spans, attrs)
|
|
19
|
+
* and document-level properties (version, meta) are compared.
|
|
20
|
+
* Pass `{ compareIds: true }` to also require matching block IDs.
|
|
21
|
+
*
|
|
22
|
+
* @param actual - The actual document
|
|
23
|
+
* @param expected - The expected document
|
|
24
|
+
* @param options - Comparison options
|
|
25
|
+
* @throws {Error} If the documents are not structurally equal
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* assertDocEqual(result.doc, expected);
|
|
30
|
+
* assertDocEqual(result.doc, expected, { compareIds: true });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertDocEqual(actual: Document, expected: Document, options?: DocEqualOptions): void;
|
|
9
34
|
/**
|
|
10
35
|
* Assert that two selections are equal.
|
|
36
|
+
*
|
|
37
|
+
* @param actual - The actual selection
|
|
38
|
+
* @param expected - The expected selection
|
|
39
|
+
* @throws {Error} If the selections differ
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* assertSelectionEqual(engine.state.selection, sel(0, 5));
|
|
44
|
+
* ```
|
|
11
45
|
*/
|
|
12
46
|
export declare function assertSelectionEqual(actual: Selection, expected: Selection): void;
|
|
13
47
|
//# sourceMappingURL=assertions.d.ts.map
|
package/dist/assertions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAe,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,eAAe,GACxB,IAAI,CA8BN;AA2ED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI,CAUjF"}
|
package/dist/assertions.js
CHANGED
|
@@ -2,20 +2,151 @@
|
|
|
2
2
|
* Custom test assertions for RTIF documents and selections.
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* Assert that two documents are structurally equal
|
|
5
|
+
* Assert that two documents are structurally equal.
|
|
6
|
+
*
|
|
7
|
+
* By default, block IDs are ignored — only block content (type, spans, attrs)
|
|
8
|
+
* and document-level properties (version, meta) are compared.
|
|
9
|
+
* Pass `{ compareIds: true }` to also require matching block IDs.
|
|
10
|
+
*
|
|
11
|
+
* @param actual - The actual document
|
|
12
|
+
* @param expected - The expected document
|
|
13
|
+
* @param options - Comparison options
|
|
14
|
+
* @throws {Error} If the documents are not structurally equal
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* assertDocEqual(result.doc, expected);
|
|
19
|
+
* assertDocEqual(result.doc, expected, { compareIds: true });
|
|
20
|
+
* ```
|
|
6
21
|
*/
|
|
7
|
-
export function assertDocEqual(
|
|
8
|
-
|
|
9
|
-
|
|
22
|
+
export function assertDocEqual(actual, expected, options) {
|
|
23
|
+
const compareIds = options?.compareIds ?? false;
|
|
24
|
+
// Version
|
|
25
|
+
if (actual.version !== expected.version) {
|
|
26
|
+
throw new Error(`Document version mismatch: got ${String(actual.version)}, expected ${String(expected.version)}`);
|
|
27
|
+
}
|
|
28
|
+
// Block count
|
|
29
|
+
if (actual.blocks.length !== expected.blocks.length) {
|
|
30
|
+
throw new Error(`Block count mismatch: got ${String(actual.blocks.length)}, expected ${String(expected.blocks.length)}`);
|
|
31
|
+
}
|
|
32
|
+
// Blocks
|
|
33
|
+
for (let i = 0; i < expected.blocks.length; i++) {
|
|
34
|
+
const aBlock = actual.blocks[i];
|
|
35
|
+
const eBlock = expected.blocks[i];
|
|
36
|
+
assertBlockEqual(aBlock, eBlock, i, compareIds);
|
|
37
|
+
}
|
|
38
|
+
// Meta
|
|
39
|
+
if (!deepEqual(actual.meta, expected.meta)) {
|
|
40
|
+
throw new Error(`Document meta mismatch:\n got: ${JSON.stringify(actual.meta)}\n expected: ${JSON.stringify(expected.meta)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Assert that two blocks are structurally equal.
|
|
45
|
+
*/
|
|
46
|
+
function assertBlockEqual(actual, expected, index, compareIds) {
|
|
47
|
+
const prefix = `Block[${String(index)}]`;
|
|
48
|
+
// ID (optional)
|
|
49
|
+
if (compareIds && actual.id !== expected.id) {
|
|
50
|
+
throw new Error(`${prefix} id mismatch: got "${actual.id}", expected "${expected.id}"`);
|
|
51
|
+
}
|
|
52
|
+
// Type
|
|
53
|
+
if (actual.type !== expected.type) {
|
|
54
|
+
throw new Error(`${prefix} type mismatch: got "${actual.type}", expected "${expected.type}"`);
|
|
55
|
+
}
|
|
56
|
+
// Span count
|
|
57
|
+
if (actual.spans.length !== expected.spans.length) {
|
|
58
|
+
throw new Error(`${prefix} span count mismatch: got ${String(actual.spans.length)}, expected ${String(expected.spans.length)}\n` +
|
|
59
|
+
` got: ${JSON.stringify(actual.spans)}\n` +
|
|
60
|
+
` expected: ${JSON.stringify(expected.spans)}`);
|
|
61
|
+
}
|
|
62
|
+
// Spans
|
|
63
|
+
for (let j = 0; j < expected.spans.length; j++) {
|
|
64
|
+
const aSpan = actual.spans[j];
|
|
65
|
+
const eSpan = expected.spans[j];
|
|
66
|
+
assertSpanEqual(aSpan, eSpan, index, j);
|
|
67
|
+
}
|
|
68
|
+
// Attrs
|
|
69
|
+
if (!deepEqual(normalizeAttrs(actual.attrs), normalizeAttrs(expected.attrs))) {
|
|
70
|
+
throw new Error(`${prefix} attrs mismatch:\n got: ${JSON.stringify(actual.attrs)}\n expected: ${JSON.stringify(expected.attrs)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Assert that two spans are structurally equal.
|
|
75
|
+
*/
|
|
76
|
+
function assertSpanEqual(actual, expected, blockIndex, spanIndex) {
|
|
77
|
+
const prefix = `Block[${String(blockIndex)}].Span[${String(spanIndex)}]`;
|
|
78
|
+
if (actual.text !== expected.text) {
|
|
79
|
+
throw new Error(`${prefix} text mismatch: got "${actual.text}", expected "${expected.text}"`);
|
|
80
|
+
}
|
|
81
|
+
if (!marksDeepEqual(actual.marks, expected.marks)) {
|
|
82
|
+
throw new Error(`${prefix} marks mismatch:\n got: ${JSON.stringify(actual.marks)}\n expected: ${JSON.stringify(expected.marks)}`);
|
|
83
|
+
}
|
|
10
84
|
}
|
|
11
85
|
/**
|
|
12
86
|
* Assert that two selections are equal.
|
|
87
|
+
*
|
|
88
|
+
* @param actual - The actual selection
|
|
89
|
+
* @param expected - The expected selection
|
|
90
|
+
* @throws {Error} If the selections differ
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* assertSelectionEqual(engine.state.selection, sel(0, 5));
|
|
95
|
+
* ```
|
|
13
96
|
*/
|
|
14
97
|
export function assertSelectionEqual(actual, expected) {
|
|
15
98
|
if (actual.anchor.offset !== expected.anchor.offset ||
|
|
16
99
|
actual.focus.offset !== expected.focus.offset) {
|
|
17
|
-
throw new Error(`Selection mismatch: got (${actual.anchor.offset}, ${actual.focus.offset}), ` +
|
|
18
|
-
`expected (${expected.anchor.offset}, ${expected.focus.offset})`);
|
|
100
|
+
throw new Error(`Selection mismatch: got (${String(actual.anchor.offset)}, ${String(actual.focus.offset)}), ` +
|
|
101
|
+
`expected (${String(expected.anchor.offset)}, ${String(expected.focus.offset)})`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Internal helpers
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
/**
|
|
108
|
+
* Normalize attrs: treat undefined and empty object as equivalent (both → undefined).
|
|
109
|
+
*/
|
|
110
|
+
function normalizeAttrs(attrs) {
|
|
111
|
+
if (attrs === undefined)
|
|
112
|
+
return undefined;
|
|
113
|
+
if (Object.keys(attrs).length === 0)
|
|
114
|
+
return undefined;
|
|
115
|
+
return attrs;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compare marks with RTIF semantics: undefined and {} are equal.
|
|
119
|
+
*/
|
|
120
|
+
function marksDeepEqual(a, b) {
|
|
121
|
+
const aNorm = a === undefined || Object.keys(a).length === 0 ? undefined : a;
|
|
122
|
+
const bNorm = b === undefined || Object.keys(b).length === 0 ? undefined : b;
|
|
123
|
+
return deepEqual(aNorm, bNorm);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Simple deep equality for JSON-serializable values.
|
|
127
|
+
*/
|
|
128
|
+
function deepEqual(a, b) {
|
|
129
|
+
if (a === b)
|
|
130
|
+
return true;
|
|
131
|
+
if (a === null || b === null)
|
|
132
|
+
return false;
|
|
133
|
+
if (a === undefined || b === undefined)
|
|
134
|
+
return false;
|
|
135
|
+
if (typeof a !== typeof b)
|
|
136
|
+
return false;
|
|
137
|
+
if (typeof a === 'object' && typeof b === 'object') {
|
|
138
|
+
const aObj = a;
|
|
139
|
+
const bObj = b;
|
|
140
|
+
const aKeys = Object.keys(aObj);
|
|
141
|
+
const bKeys = Object.keys(bObj);
|
|
142
|
+
if (aKeys.length !== bKeys.length)
|
|
143
|
+
return false;
|
|
144
|
+
for (const key of aKeys) {
|
|
145
|
+
if (!deepEqual(aObj[key], bObj[key]))
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
19
149
|
}
|
|
150
|
+
return false;
|
|
20
151
|
}
|
|
21
152
|
//# sourceMappingURL=assertions.js.map
|
package/dist/assertions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"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"}
|
package/dist/builders.d.ts
CHANGED
|
@@ -1,25 +1,214 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fluent document builders for tests.
|
|
3
|
-
*
|
|
2
|
+
* Fluent document builders and fixture factories for RTIF tests.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { doc, span, sel, emptyDoc, textDoc } from '@rtif-sdk/test-kit';
|
|
7
|
+
*
|
|
8
|
+
* // Plain text block
|
|
9
|
+
* const d1 = doc().block('Hello').build();
|
|
10
|
+
*
|
|
11
|
+
* // Block with formatted spans
|
|
12
|
+
* const d2 = doc()
|
|
13
|
+
* .block(span('Hello '), span('world', { bold: true }), span('!'))
|
|
14
|
+
* .build();
|
|
15
|
+
*
|
|
16
|
+
* // Block with type, attrs, and custom ID
|
|
17
|
+
* const d3 = doc()
|
|
18
|
+
* .block('Title').type('heading').attrs({ level: 1 }).id('h1')
|
|
19
|
+
* .block('Body text')
|
|
20
|
+
* .meta({ title: 'My Doc' })
|
|
21
|
+
* .build();
|
|
22
|
+
*
|
|
23
|
+
* // Common fixtures
|
|
24
|
+
* const empty = emptyDoc();
|
|
25
|
+
* const simple = textDoc('hello');
|
|
26
|
+
* ```
|
|
4
27
|
*/
|
|
5
28
|
import type { Document, Selection } from '@rtif-sdk/core';
|
|
6
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Input type for span content in `block()`.
|
|
31
|
+
* A plain string creates an unmarked span; a `SpanDef` carries marks.
|
|
32
|
+
*/
|
|
33
|
+
export type SpanInput = string | SpanDef;
|
|
34
|
+
/**
|
|
35
|
+
* A span definition with text and optional marks.
|
|
36
|
+
* Created via the `span()` helper.
|
|
37
|
+
*/
|
|
38
|
+
export interface SpanDef {
|
|
39
|
+
readonly text: string;
|
|
40
|
+
readonly marks?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a span definition with text and optional marks.
|
|
44
|
+
*
|
|
45
|
+
* @param text - The span text content
|
|
46
|
+
* @param marks - Optional formatting marks
|
|
47
|
+
* @returns A SpanDef for use in `DocumentBuilder.block()`
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* span('hello') // plain text
|
|
52
|
+
* span('bold', { bold: true }) // bold text
|
|
53
|
+
* span('link', { link: { href: '…' }}) // link
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function span(text: string, marks?: Record<string, unknown>): SpanDef;
|
|
57
|
+
/** Builder for constructing test documents fluently. */
|
|
7
58
|
export declare class DocumentBuilder {
|
|
8
|
-
private
|
|
59
|
+
private _blocks;
|
|
60
|
+
private _meta?;
|
|
9
61
|
private nextId;
|
|
10
62
|
/**
|
|
11
|
-
* Add a block with
|
|
63
|
+
* Add a block with one or more spans.
|
|
64
|
+
*
|
|
65
|
+
* When called with a single string, creates a plain text block.
|
|
66
|
+
* When called with `SpanInput` values (strings and/or `span()` results),
|
|
67
|
+
* each input becomes a span in the block.
|
|
68
|
+
*
|
|
69
|
+
* @param inputs - One or more span inputs (strings or SpanDefs)
|
|
70
|
+
* @returns The builder for chaining
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* doc().block('Hello') // plain text
|
|
75
|
+
* doc().block(span('Hello '), span('world', { bold: true })) // mixed
|
|
76
|
+
* doc().block('plain ', span('bold', { bold: true }), ' more') // strings + spans
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
block(...inputs: SpanInput[]): this;
|
|
80
|
+
/**
|
|
81
|
+
* Set the block type on the last added block.
|
|
82
|
+
*
|
|
83
|
+
* @param blockType - The block type string (e.g., 'heading', 'code_block')
|
|
84
|
+
* @returns The builder for chaining
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* doc().block('Title').type('heading').build()
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
type(blockType: string): this;
|
|
92
|
+
/**
|
|
93
|
+
* Set block-level attributes on the last added block.
|
|
94
|
+
*
|
|
95
|
+
* @param blockAttrs - The attributes to set
|
|
96
|
+
* @returns The builder for chaining
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* doc().block('Title').type('heading').attrs({ level: 2 }).build()
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
attrs(blockAttrs: Record<string, unknown>): this;
|
|
104
|
+
/**
|
|
105
|
+
* Set a custom ID on the last added block.
|
|
12
106
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
107
|
+
* @param blockId - The block ID
|
|
108
|
+
* @returns The builder for chaining
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* doc().block('Hello').id('custom-id').build()
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
id(blockId: string): this;
|
|
116
|
+
/**
|
|
117
|
+
* Set document-level metadata.
|
|
118
|
+
*
|
|
119
|
+
* @param docMeta - The metadata key-value pairs
|
|
120
|
+
* @returns The builder for chaining
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* doc().block('Hello').meta({ title: 'My Doc', lang: 'en' }).build()
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
meta(docMeta: Record<string, unknown>): this;
|
|
128
|
+
/**
|
|
129
|
+
* Build the final Document.
|
|
130
|
+
*
|
|
131
|
+
* @returns A valid RTIF Document
|
|
16
132
|
*/
|
|
17
|
-
block(text: string, _marks?: Record<string, string>): this;
|
|
18
|
-
/** Build the final Document */
|
|
19
133
|
build(): Document;
|
|
20
134
|
}
|
|
21
|
-
/**
|
|
135
|
+
/**
|
|
136
|
+
* Create a new DocumentBuilder.
|
|
137
|
+
*
|
|
138
|
+
* @returns A fresh DocumentBuilder
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const d = doc().block('Hello').block('World').build();
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
22
145
|
export declare function doc(): DocumentBuilder;
|
|
23
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* Create a Selection from anchor and focus offsets.
|
|
148
|
+
*
|
|
149
|
+
* @param anchor - Anchor offset
|
|
150
|
+
* @param focus - Focus offset
|
|
151
|
+
* @returns A Selection object
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* sel(0, 5) // forward selection
|
|
156
|
+
* sel(5, 5) // collapsed cursor at offset 5
|
|
157
|
+
* sel(10, 5) // backward selection
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
24
160
|
export declare function sel(anchor: number, focus: number): Selection;
|
|
161
|
+
/**
|
|
162
|
+
* Create an empty document with a single empty block.
|
|
163
|
+
*
|
|
164
|
+
* @param blockId - Optional block ID (default: 'b1')
|
|
165
|
+
* @returns A Document with one empty text block
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* const d = emptyDoc();
|
|
170
|
+
* // { version: 1, blocks: [{ id: 'b1', type: 'text', spans: [{ text: '' }] }] }
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function emptyDoc(blockId?: string): Document;
|
|
174
|
+
/**
|
|
175
|
+
* Create a document with a single text block.
|
|
176
|
+
*
|
|
177
|
+
* @param text - The block text content
|
|
178
|
+
* @param blockId - Optional block ID (default: 'b1')
|
|
179
|
+
* @returns A Document with one text block
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* const d = textDoc('hello');
|
|
184
|
+
* const d2 = textDoc('hello', 'custom-id');
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare function textDoc(text: string, blockId?: string): Document;
|
|
188
|
+
/**
|
|
189
|
+
* Create a document with multiple text blocks.
|
|
190
|
+
*
|
|
191
|
+
* @param texts - Array of text strings, one per block
|
|
192
|
+
* @returns A Document with blocks 'b1', 'b2', etc.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const d = multiBlockDoc('hello', 'world');
|
|
197
|
+
* // Two blocks: b1="hello", b2="world"
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export declare function multiBlockDoc(...texts: string[]): Document;
|
|
201
|
+
/**
|
|
202
|
+
* Extract plain text from a document, joining blocks with newlines.
|
|
203
|
+
*
|
|
204
|
+
* @param document - The RTIF document
|
|
205
|
+
* @returns Plain text representation
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* const text = docText(engine.state.doc);
|
|
210
|
+
* // "hello\nworld"
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
export declare function docText(document: Document): string;
|
|
25
214
|
//# sourceMappingURL=builders.d.ts.map
|
package/dist/builders.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAe,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAK3E;AAED,wDAAwD;AACxD,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,KAAK,CAAC,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAK;IAEnB;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI;IAyBnC;;;;;;;;;;OAUG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAS7B;;;;;;;;;;OAUG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAShD;;;;;;;;;;OAUG;IACH,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IASzB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5C;;;;OAIG;IACH,KAAK,IAAI,QAAQ;CAclB;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,IAAI,eAAe,CAErC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAK5D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,OAAO,SAAO,GAAG,QAAQ,CAKjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAO,GAAG,QAAQ,CAK9D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,CAY1D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAIlD"}
|
package/dist/builders.js
CHANGED
|
@@ -1,49 +1,302 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fluent document builders for tests.
|
|
3
|
-
*
|
|
2
|
+
* Fluent document builders and fixture factories for RTIF tests.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { doc, span, sel, emptyDoc, textDoc } from '@rtif-sdk/test-kit';
|
|
7
|
+
*
|
|
8
|
+
* // Plain text block
|
|
9
|
+
* const d1 = doc().block('Hello').build();
|
|
10
|
+
*
|
|
11
|
+
* // Block with formatted spans
|
|
12
|
+
* const d2 = doc()
|
|
13
|
+
* .block(span('Hello '), span('world', { bold: true }), span('!'))
|
|
14
|
+
* .build();
|
|
15
|
+
*
|
|
16
|
+
* // Block with type, attrs, and custom ID
|
|
17
|
+
* const d3 = doc()
|
|
18
|
+
* .block('Title').type('heading').attrs({ level: 1 }).id('h1')
|
|
19
|
+
* .block('Body text')
|
|
20
|
+
* .meta({ title: 'My Doc' })
|
|
21
|
+
* .build();
|
|
22
|
+
*
|
|
23
|
+
* // Common fixtures
|
|
24
|
+
* const empty = emptyDoc();
|
|
25
|
+
* const simple = textDoc('hello');
|
|
26
|
+
* ```
|
|
4
27
|
*/
|
|
5
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Create a span definition with text and optional marks.
|
|
30
|
+
*
|
|
31
|
+
* @param text - The span text content
|
|
32
|
+
* @param marks - Optional formatting marks
|
|
33
|
+
* @returns A SpanDef for use in `DocumentBuilder.block()`
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* span('hello') // plain text
|
|
38
|
+
* span('bold', { bold: true }) // bold text
|
|
39
|
+
* span('link', { link: { href: '…' }}) // link
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function span(text, marks) {
|
|
43
|
+
if (marks !== undefined) {
|
|
44
|
+
return { text, marks };
|
|
45
|
+
}
|
|
46
|
+
return { text };
|
|
47
|
+
}
|
|
48
|
+
/** Builder for constructing test documents fluently. */
|
|
6
49
|
export class DocumentBuilder {
|
|
7
|
-
|
|
50
|
+
_blocks = [];
|
|
51
|
+
_meta;
|
|
8
52
|
nextId = 1;
|
|
9
53
|
/**
|
|
10
|
-
* Add a block with
|
|
54
|
+
* Add a block with one or more spans.
|
|
55
|
+
*
|
|
56
|
+
* When called with a single string, creates a plain text block.
|
|
57
|
+
* When called with `SpanInput` values (strings and/or `span()` results),
|
|
58
|
+
* each input becomes a span in the block.
|
|
11
59
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
60
|
+
* @param inputs - One or more span inputs (strings or SpanDefs)
|
|
61
|
+
* @returns The builder for chaining
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* doc().block('Hello') // plain text
|
|
66
|
+
* doc().block(span('Hello '), span('world', { bold: true })) // mixed
|
|
67
|
+
* doc().block('plain ', span('bold', { bold: true }), ' more') // strings + spans
|
|
68
|
+
* ```
|
|
15
69
|
*/
|
|
16
|
-
block(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
70
|
+
block(...inputs) {
|
|
71
|
+
const spans = inputs.map((input) => {
|
|
72
|
+
if (typeof input === 'string') {
|
|
73
|
+
return { text: input };
|
|
74
|
+
}
|
|
75
|
+
const s = { text: input.text };
|
|
76
|
+
if (input.marks !== undefined) {
|
|
77
|
+
s.marks = { ...input.marks };
|
|
78
|
+
}
|
|
79
|
+
return s;
|
|
80
|
+
});
|
|
81
|
+
// Default to an empty span if no inputs provided
|
|
82
|
+
if (spans.length === 0) {
|
|
83
|
+
spans.push({ text: '' });
|
|
84
|
+
}
|
|
85
|
+
this._blocks.push({
|
|
20
86
|
id: `b${this.nextId++}`,
|
|
21
87
|
type: 'text',
|
|
22
|
-
spans
|
|
88
|
+
spans,
|
|
23
89
|
});
|
|
24
90
|
return this;
|
|
25
91
|
}
|
|
26
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Set the block type on the last added block.
|
|
94
|
+
*
|
|
95
|
+
* @param blockType - The block type string (e.g., 'heading', 'code_block')
|
|
96
|
+
* @returns The builder for chaining
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* doc().block('Title').type('heading').build()
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
type(blockType) {
|
|
104
|
+
const last = this._blocks[this._blocks.length - 1];
|
|
105
|
+
if (!last) {
|
|
106
|
+
throw new Error('type() must be called after block()');
|
|
107
|
+
}
|
|
108
|
+
last.type = blockType;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Set block-level attributes on the last added block.
|
|
113
|
+
*
|
|
114
|
+
* @param blockAttrs - The attributes to set
|
|
115
|
+
* @returns The builder for chaining
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* doc().block('Title').type('heading').attrs({ level: 2 }).build()
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
attrs(blockAttrs) {
|
|
123
|
+
const last = this._blocks[this._blocks.length - 1];
|
|
124
|
+
if (!last) {
|
|
125
|
+
throw new Error('attrs() must be called after block()');
|
|
126
|
+
}
|
|
127
|
+
last.attrs = { ...blockAttrs };
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Set a custom ID on the last added block.
|
|
132
|
+
*
|
|
133
|
+
* @param blockId - The block ID
|
|
134
|
+
* @returns The builder for chaining
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* doc().block('Hello').id('custom-id').build()
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
id(blockId) {
|
|
142
|
+
const last = this._blocks[this._blocks.length - 1];
|
|
143
|
+
if (!last) {
|
|
144
|
+
throw new Error('id() must be called after block()');
|
|
145
|
+
}
|
|
146
|
+
last.id = blockId;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Set document-level metadata.
|
|
151
|
+
*
|
|
152
|
+
* @param docMeta - The metadata key-value pairs
|
|
153
|
+
* @returns The builder for chaining
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* doc().block('Hello').meta({ title: 'My Doc', lang: 'en' }).build()
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
meta(docMeta) {
|
|
161
|
+
this._meta = { ...docMeta };
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Build the final Document.
|
|
166
|
+
*
|
|
167
|
+
* @returns A valid RTIF Document
|
|
168
|
+
*/
|
|
27
169
|
build() {
|
|
28
|
-
if (this.
|
|
29
|
-
this.
|
|
170
|
+
if (this._blocks.length === 0) {
|
|
171
|
+
this._blocks.push({
|
|
30
172
|
id: 'b1',
|
|
31
173
|
type: 'text',
|
|
32
174
|
spans: [{ text: '' }],
|
|
33
175
|
});
|
|
34
176
|
}
|
|
35
|
-
|
|
177
|
+
const result = { version: 1, blocks: this._blocks };
|
|
178
|
+
if (this._meta !== undefined) {
|
|
179
|
+
result.meta = this._meta;
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
36
182
|
}
|
|
37
183
|
}
|
|
38
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Create a new DocumentBuilder.
|
|
186
|
+
*
|
|
187
|
+
* @returns A fresh DocumentBuilder
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* const d = doc().block('Hello').block('World').build();
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
39
194
|
export function doc() {
|
|
40
195
|
return new DocumentBuilder();
|
|
41
196
|
}
|
|
42
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* Create a Selection from anchor and focus offsets.
|
|
199
|
+
*
|
|
200
|
+
* @param anchor - Anchor offset
|
|
201
|
+
* @param focus - Focus offset
|
|
202
|
+
* @returns A Selection object
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* sel(0, 5) // forward selection
|
|
207
|
+
* sel(5, 5) // collapsed cursor at offset 5
|
|
208
|
+
* sel(10, 5) // backward selection
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
43
211
|
export function sel(anchor, focus) {
|
|
44
212
|
return {
|
|
45
213
|
anchor: { offset: anchor },
|
|
46
214
|
focus: { offset: focus },
|
|
47
215
|
};
|
|
48
216
|
}
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Common Fixtures
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
/**
|
|
221
|
+
* Create an empty document with a single empty block.
|
|
222
|
+
*
|
|
223
|
+
* @param blockId - Optional block ID (default: 'b1')
|
|
224
|
+
* @returns A Document with one empty text block
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* const d = emptyDoc();
|
|
229
|
+
* // { version: 1, blocks: [{ id: 'b1', type: 'text', spans: [{ text: '' }] }] }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export function emptyDoc(blockId = 'b1') {
|
|
233
|
+
return {
|
|
234
|
+
version: 1,
|
|
235
|
+
blocks: [{ id: blockId, type: 'text', spans: [{ text: '' }] }],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Create a document with a single text block.
|
|
240
|
+
*
|
|
241
|
+
* @param text - The block text content
|
|
242
|
+
* @param blockId - Optional block ID (default: 'b1')
|
|
243
|
+
* @returns A Document with one text block
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* const d = textDoc('hello');
|
|
248
|
+
* const d2 = textDoc('hello', 'custom-id');
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
export function textDoc(text, blockId = 'b1') {
|
|
252
|
+
return {
|
|
253
|
+
version: 1,
|
|
254
|
+
blocks: [{ id: blockId, type: 'text', spans: [{ text }] }],
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Create a document with multiple text blocks.
|
|
259
|
+
*
|
|
260
|
+
* @param texts - Array of text strings, one per block
|
|
261
|
+
* @returns A Document with blocks 'b1', 'b2', etc.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* const d = multiBlockDoc('hello', 'world');
|
|
266
|
+
* // Two blocks: b1="hello", b2="world"
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
export function multiBlockDoc(...texts) {
|
|
270
|
+
if (texts.length === 0) {
|
|
271
|
+
return emptyDoc();
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
version: 1,
|
|
275
|
+
blocks: texts.map((text, i) => ({
|
|
276
|
+
id: `b${i + 1}`,
|
|
277
|
+
type: 'text',
|
|
278
|
+
spans: [{ text }],
|
|
279
|
+
})),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
// Helpers
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
/**
|
|
286
|
+
* Extract plain text from a document, joining blocks with newlines.
|
|
287
|
+
*
|
|
288
|
+
* @param document - The RTIF document
|
|
289
|
+
* @returns Plain text representation
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* const text = docText(engine.state.doc);
|
|
294
|
+
* // "hello\nworld"
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
export function docText(document) {
|
|
298
|
+
return document.blocks
|
|
299
|
+
.map((b) => b.spans.map((s) => s.text).join(''))
|
|
300
|
+
.join('\n');
|
|
301
|
+
}
|
|
49
302
|
//# sourceMappingURL=builders.js.map
|
package/dist/builders.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { doc, sel, DocumentBuilder } from './builders.js';
|
|
1
|
+
export { doc, sel, span, DocumentBuilder, emptyDoc, textDoc, multiBlockDoc, docText, } from './builders.js';
|
|
2
|
+
export type { SpanInput, SpanDef } from './builders.js';
|
|
2
3
|
export { assertDocEqual, assertSelectionEqual } from './assertions.js';
|
|
4
|
+
export type { DocEqualOptions } from './assertions.js';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,GAAG,EACH,GAAG,EACH,IAAI,EACJ,eAAe,EACf,QAAQ,EACR,OAAO,EACP,aAAa,EACb,OAAO,GACR,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// Builders
|
|
2
|
+
export { doc, sel, span, DocumentBuilder, emptyDoc, textDoc, multiBlockDoc, docText, } from './builders.js';
|
|
3
|
+
// Assertions
|
|
2
4
|
export { assertDocEqual, assertSelectionEqual } from './assertions.js';
|
|
3
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EACL,GAAG,EACH,GAAG,EACH,IAAI,EACJ,eAAe,EACf,QAAQ,EACR,OAAO,EACP,aAAa,EACb,OAAO,GACR,MAAM,eAAe,CAAC;AAGvB,aAAa;AACb,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtif-sdk/test-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "RTIF shared test utilities, builders, and assertions",
|
|
5
5
|
"author": "coryrobinson42@gmail.com",
|
|
6
6
|
"type": "module",
|
|
@@ -12,10 +12,19 @@
|
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"files": [
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
16
18
|
"sideEffects": false,
|
|
17
|
-
"engines": {
|
|
18
|
-
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20.0.0"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"rtif",
|
|
24
|
+
"test-utilities",
|
|
25
|
+
"rich-text",
|
|
26
|
+
"editor-testing"
|
|
27
|
+
],
|
|
19
28
|
"scripts": {
|
|
20
29
|
"build": "tsc --build tsconfig.build.json",
|
|
21
30
|
"test": "vitest run",
|