@hydranium/conformance 1.0.0-next.28 → 1.0.0-next.280
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/README.md +79 -74
- package/lib/conformance-suite.js +1 -1
- package/lib/data/index.d.ts +37 -13
- package/lib/data/index.d.ts.map +1 -1
- package/lib/data/index.js +839 -34
- package/lib/data/index.js.map +1 -1
- package/lib/glsp/index.d.ts +24 -4
- package/lib/glsp/index.d.ts.map +1 -1
- package/lib/glsp/index.js +34 -2
- package/lib/glsp/index.js.map +1 -1
- package/lib/glsp/node/index.d.ts +64 -0
- package/lib/glsp/node/index.d.ts.map +1 -0
- package/lib/glsp/node/index.js +195 -0
- package/lib/glsp/node/index.js.map +1 -0
- package/lib/jest/index.js +3 -3
- package/lib/jest/index.js.map +1 -1
- package/lib/lsp/index.d.ts +23 -3
- package/lib/lsp/index.d.ts.map +1 -1
- package/lib/lsp/index.js +69 -3
- package/lib/lsp/index.js.map +1 -1
- package/lib/model.d.ts +131 -8
- package/lib/model.d.ts.map +1 -1
- package/lib/model.js.map +1 -1
- package/lib/vitest/index.js +3 -3
- package/lib/vitest/index.js.map +1 -1
- package/package.json +32 -45
- package/src/conformance-suite.ts +1 -1
- package/src/data/index.ts +1067 -47
- package/src/glsp/index.ts +64 -6
- package/src/glsp/node/index.ts +274 -0
- package/src/jest/index.ts +3 -3
- package/src/lsp/index.ts +110 -7
- package/src/model.ts +134 -8
- package/src/vitest/index.ts +3 -3
package/src/model.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
+
import type { Locale } from '@hydranium/protocol';
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* A fixture value that may be given directly or DEFERRED to check time.
|
|
12
14
|
*
|
|
@@ -79,12 +81,53 @@ export function resolveModel(model: ConformanceModel): ResolvedConformanceModel
|
|
|
79
81
|
export interface EditSpec {
|
|
80
82
|
/** Replacement model text; {@link Deferred} for the same reasons as `ConformanceModel.text`. */
|
|
81
83
|
readonly to: Deferred<string>;
|
|
84
|
+
/** True of the root after the edit and false of `valid`'s root, so a check can tell the two apart. */
|
|
82
85
|
readonly expect: (root: unknown) => boolean;
|
|
83
86
|
}
|
|
84
87
|
|
|
88
|
+
/**
|
|
89
|
+
* A reference-picker query for an element that does not exist yet, plus the
|
|
90
|
+
* candidate the adopter expects it to offer.
|
|
91
|
+
*
|
|
92
|
+
* **The URI a create-element flow holds is a FOLDER**, because the file is not
|
|
93
|
+
* written until the dialog is confirmed. A folder URI names no file and so
|
|
94
|
+
* carries no extension, which is the one shape a head cannot route to a grammar
|
|
95
|
+
* by URI alone — it has to resolve the language some other way. That makes this
|
|
96
|
+
* the create dialog's load-bearing precondition and the reason the query is
|
|
97
|
+
* worth a conformance check of its own: a head that gets it wrong answers no
|
|
98
|
+
* candidates or throws, and the dialog never opens.
|
|
99
|
+
*/
|
|
100
|
+
export interface ReferenceQuerySpec {
|
|
101
|
+
/** AST type of the element being created — the synthetic source's own type. */
|
|
102
|
+
readonly type: string;
|
|
103
|
+
/** The reference property on the source (or on `syntheticPath`'s leaf) whose candidates the picker fills. */
|
|
104
|
+
readonly property: string;
|
|
105
|
+
/**
|
|
106
|
+
* Steps from the synthetic source down to the node holding `property`, when
|
|
107
|
+
* the reference is not on the source itself. Each step is
|
|
108
|
+
* `[containerProperty, type]` — the kit builds the `SyntheticStep`s, so the
|
|
109
|
+
* fixture names no protocol type.
|
|
110
|
+
*/
|
|
111
|
+
readonly path?: ReadonlyArray<readonly [containerProperty: string, type: string]>;
|
|
112
|
+
/**
|
|
113
|
+
* Folder the create flow asks at. {@link Deferred} because a fixture may name
|
|
114
|
+
* a workspace the driver's `connect` only just created. Defaults to the parent
|
|
115
|
+
* of `valid.uri`, which is the folder a sibling of the valid model would go
|
|
116
|
+
* into — the common case, so most fixtures supply only `type` + `property`.
|
|
117
|
+
*/
|
|
118
|
+
readonly folderUri?: Deferred<string>;
|
|
119
|
+
/**
|
|
120
|
+
* A candidate label the query MUST offer. Without it an empty result passes,
|
|
121
|
+
* and empty is exactly what the defect this check exists for produces — so
|
|
122
|
+
* the expectation is what makes the check discriminating rather than a
|
|
123
|
+
* smoke test.
|
|
124
|
+
*/
|
|
125
|
+
readonly expectCandidate: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
85
128
|
/**
|
|
86
129
|
* The per-language fixture. `valid` and `invalid` are defined once and reused
|
|
87
|
-
* across heads;
|
|
130
|
+
* across heads; every other field is a per-head opt-in.
|
|
88
131
|
*
|
|
89
132
|
* Requiring BOTH a valid and an invalid model is the false-green guard — a
|
|
90
133
|
* vacuous "invalid" model that actually parses clean fails the diagnostics
|
|
@@ -98,13 +141,18 @@ export interface EditSpec {
|
|
|
98
141
|
* uses `invalid.text` and never calls `edit.expect`, so an LSP-only adopter
|
|
99
142
|
* has nothing to supply here.
|
|
100
143
|
* - `completionPosition` — read by the **LSP slice only**.
|
|
144
|
+
* - `referenceQuery` — read by the **data slice only**, and only when the
|
|
145
|
+
* driver supplies `references` (the reference surface is opt-in on the head
|
|
146
|
+
* too, so both halves have to be present for the check to run).
|
|
147
|
+
* - `dependent` and `breakingEdit` — read by the **data slice only**.
|
|
148
|
+
* - `renderedDiagnostic` — read by the **LSP slice only**.
|
|
101
149
|
*
|
|
102
|
-
*
|
|
103
|
-
* rather than silently not running. Making
|
|
104
|
-
* shared fixture: the head that ignores the field has to invent a value
|
|
105
|
-
* and the two heads' inventions drift apart until one fixture
|
|
106
|
-
* other. A required field that one head ignores does not
|
|
107
|
-
* fixture; it produces a misleading one.
|
|
150
|
+
* The opt-ins are optional and their checks report *skipped* when absent,
|
|
151
|
+
* rather than silently not running. Making any of them mandatory would defeat
|
|
152
|
+
* the shared fixture: the head that ignores the field has to invent a value
|
|
153
|
+
* for it, and the two heads' inventions drift apart until one fixture
|
|
154
|
+
* contradicts the other. A required field that one head ignores does not
|
|
155
|
+
* produce a shared fixture; it produces a misleading one.
|
|
108
156
|
*
|
|
109
157
|
* The GLSP fixture is separate (per-diagram-type, generic over the adopter
|
|
110
158
|
* action type) — see `./glsp`.
|
|
@@ -114,9 +162,87 @@ export interface LanguageFixture {
|
|
|
114
162
|
readonly invalid: ConformanceModel;
|
|
115
163
|
/**
|
|
116
164
|
* Optional: an edit plus its observability assertion. The data slice's
|
|
117
|
-
*
|
|
165
|
+
* checks that need a second, observably different text run only when it is
|
|
166
|
+
* supplied.
|
|
118
167
|
*/
|
|
119
168
|
readonly edit?: EditSpec;
|
|
120
169
|
/** Optional: a position at which the LSP completion check requests completion. */
|
|
121
170
|
readonly completionPosition?: { readonly line: number; readonly character: number };
|
|
171
|
+
/**
|
|
172
|
+
* Optional: the create-dialog reference query. Read by the **data slice
|
|
173
|
+
* only**, and only when the driver exposes the opt-in reference surface.
|
|
174
|
+
*/
|
|
175
|
+
readonly referenceQuery?: ReferenceQuerySpec;
|
|
176
|
+
/**
|
|
177
|
+
* Optional: a second document that REFERENCES {@link valid}, so the data
|
|
178
|
+
* slice can provoke a CASCADE — a rebuild of this document caused by
|
|
179
|
+
* editing the one it points at, with its own text never touched.
|
|
180
|
+
*
|
|
181
|
+
* Opt-in because a grammar need not have cross-document references at all,
|
|
182
|
+
* and because only the adopter knows which pair of documents forms one.
|
|
183
|
+
* Supplying it IS the claim that editing `valid` rebuilds this document; the
|
|
184
|
+
* check then holds the head to reporting that on `onDocumentsBuilt`, which
|
|
185
|
+
* is the only channel that can carry it — the document has no subscriber and
|
|
186
|
+
* its file did not change, so neither the update channel nor a filesystem
|
|
187
|
+
* watcher can.
|
|
188
|
+
*/
|
|
189
|
+
readonly dependent?: ConformanceModel;
|
|
190
|
+
/**
|
|
191
|
+
* Optional: a replacement text for {@link valid} that changes what
|
|
192
|
+
* {@link dependent} shows, typically by breaking its reference into
|
|
193
|
+
* `valid`, so that the dependent reports a diagnostic. Read by the **data
|
|
194
|
+
* slice only**, and only with `dependent`.
|
|
195
|
+
*
|
|
196
|
+
* {@link edit} does not serve here: an edit that leaves the dependent as it
|
|
197
|
+
* was may send a watcher of the dependent nothing, as the framework's head
|
|
198
|
+
* suppresses an update that changes nothing observable. Supplying this is
|
|
199
|
+
* the claim that writing it makes a watched `dependent` receive an update
|
|
200
|
+
* event, which the check then holds to being a rebuild that names no
|
|
201
|
+
* client.
|
|
202
|
+
*/
|
|
203
|
+
readonly breakingEdit?: Deferred<string>;
|
|
204
|
+
/**
|
|
205
|
+
* Optional: a locale plus the sentence the server must publish in it. Read
|
|
206
|
+
* by the **LSP slice only**.
|
|
207
|
+
*/
|
|
208
|
+
readonly renderedDiagnostic?: RenderedDiagnosticSpec;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* A locale, and one sentence the server must produce in it for the `invalid`
|
|
213
|
+
* fixture.
|
|
214
|
+
*
|
|
215
|
+
* **Opt-in, and it has to be.** The framework ships no catalogue and selects no
|
|
216
|
+
* locale, so a server that installs no renderer correctly publishes English —
|
|
217
|
+
* mandating this check would fail every adopter without i18n for doing the right
|
|
218
|
+
* thing. Supplying the field is the adopter saying "I render server-side, hold me
|
|
219
|
+
* to it".
|
|
220
|
+
*
|
|
221
|
+
* `expected` is a SUBSTRING, not the whole message. The kit owns no grammar, so
|
|
222
|
+
* it cannot know how many diagnostics `invalid` produces or in what order, and
|
|
223
|
+
* an adopter should be able to pin the translated fragment without restating a
|
|
224
|
+
* sentence they may reword. A substring long enough to be wrong if the render
|
|
225
|
+
* did not happen is the whole requirement.
|
|
226
|
+
*
|
|
227
|
+
* `absentWithLocale` is what makes the check a pair rather than a single
|
|
228
|
+
* assertion: "the message contains X" also passes for a server whose English
|
|
229
|
+
* happens to contain X, and for one that renders regardless of locale. Naming
|
|
230
|
+
* the fragment that must disappear once the locale is declared is what
|
|
231
|
+
* distinguishes those.
|
|
232
|
+
*/
|
|
233
|
+
export interface RenderedDiagnosticSpec {
|
|
234
|
+
/** The locale to declare at `initialize` — the tag whose catalogue the server has. */
|
|
235
|
+
readonly locale: Locale;
|
|
236
|
+
/** A fragment of the translated sentence, present in some diagnostic of the `invalid` fixture. */
|
|
237
|
+
readonly expected: string;
|
|
238
|
+
/**
|
|
239
|
+
* A fragment that must be absent once `locale` is declared, and present
|
|
240
|
+
* without it — normally a piece of the server's own English.
|
|
241
|
+
*
|
|
242
|
+
* Optional only because a catalogue may translate a message whose English
|
|
243
|
+
* shares no distinctive fragment with it. Omitting it drops the second half
|
|
244
|
+
* of the pair and leaves a check that a render-nothing server can pass; the
|
|
245
|
+
* kit reports that rather than pretending otherwise.
|
|
246
|
+
*/
|
|
247
|
+
readonly absentWithLocale?: string;
|
|
122
248
|
}
|
package/src/vitest/index.ts
CHANGED
|
@@ -57,7 +57,7 @@ const vitestRunner: ConformanceRunner = {
|
|
|
57
57
|
export function runDataConformance<TTransfer extends TransferElement, TDiagnostic extends TransferDiagnostic = TransferDiagnostic>(
|
|
58
58
|
options: DataConformanceOptions<TTransfer, TDiagnostic>
|
|
59
59
|
): void {
|
|
60
|
-
emitConformanceSuite(vitestRunner, options.suiteTitle ?? '
|
|
60
|
+
emitConformanceSuite(vitestRunner, options.suiteTitle ?? 'data-server conformance', buildDataChecks(options));
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
@@ -66,7 +66,7 @@ export function runDataConformance<TTransfer extends TransferElement, TDiagnosti
|
|
|
66
66
|
* per language) and a ran-vs-skipped summary.
|
|
67
67
|
*/
|
|
68
68
|
export function runLspConformance(options: LspConformanceOptions): void {
|
|
69
|
-
emitConformanceSuite(vitestRunner, options.suiteTitle ?? 'conformance
|
|
69
|
+
emitConformanceSuite(vitestRunner, options.suiteTitle ?? 'lsp conformance', buildLspChecks(options));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -77,5 +77,5 @@ export function runLspConformance(options: LspConformanceOptions): void {
|
|
|
77
77
|
export function runGlspConformance<TAction, TDriver extends GlspConformanceDriver<TAction>>(
|
|
78
78
|
options: GlspConformanceOptions<TAction, TDriver>
|
|
79
79
|
): void {
|
|
80
|
-
emitConformanceSuite(vitestRunner, options.suiteTitle ?? 'conformance
|
|
80
|
+
emitConformanceSuite(vitestRunner, options.suiteTitle ?? 'glsp conformance', buildGlspChecks(options));
|
|
81
81
|
}
|