@hyperfixi/testing-framework 4.1.0 → 4.3.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/CHANGELOG.md +246 -1
- package/package.json +7 -7
- package/src/multilingual/command-shapes-gate.ts +76 -0
- package/src/multilingual/command-shapes.1.test.ts +9 -0
- package/src/multilingual/command-shapes.2.test.ts +9 -0
- package/src/multilingual/command-shapes.3.test.ts +9 -0
- package/src/multilingual/command-shapes.cases.json +6816 -0
- package/src/multilingual/command-shapes.cases.test.ts +122 -0
- package/src/multilingual/command-shapes.oracle.test.ts +204 -0
- package/src/multilingual/command-shapes.ts +919 -0
- package/src/multilingual/direct-path-shapes.ts +9 -2
- package/src/multilingual/value-matrix.branch-phrases.test.ts +13 -0
- package/src/multilingual/value-matrix.branch.test.ts +13 -0
- package/src/multilingual/value-matrix.ts +56 -10
|
@@ -376,8 +376,15 @@ export async function initDirectPathHosts(): Promise<DirectPathHosts> {
|
|
|
376
376
|
for (const steps of scriptOf(c)) parts.push(await run(host, c, code, lang, steps));
|
|
377
377
|
return parts.join(' ‖ ');
|
|
378
378
|
};
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
// Two failure channels: parse errors, and the tokenizer THROWING on a
|
|
380
|
+
// character it does not know (the text a refused translation leaves as written).
|
|
381
|
+
const rejects = (host: ScriptHost, code: string): string | undefined => {
|
|
382
|
+
try {
|
|
383
|
+
return host.parse(code)?.errors?.[0]?.message.split('\n')[0];
|
|
384
|
+
} catch (e) {
|
|
385
|
+
return (e as Error).message.split('\n')[0] ?? 'threw';
|
|
386
|
+
}
|
|
387
|
+
};
|
|
381
388
|
|
|
382
389
|
return {
|
|
383
390
|
async runCase(c) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value matrix gate — the `branch` position's operator-phrase cells (`is equal
|
|
3
|
+
* to`, `includes`, `is an Element`, …). See value-matrix.ts for the matrix
|
|
4
|
+
* and value-matrix-gate.ts for the assertions.
|
|
5
|
+
*
|
|
6
|
+
* @vitest-environment node
|
|
7
|
+
* Required, not a preference: under happy-dom the engines bind happy-dom's
|
|
8
|
+
* DOM constructors, not the jsdom window's (see
|
|
9
|
+
* shipped-examples-execution.test.ts).
|
|
10
|
+
*/
|
|
11
|
+
import { describeValueMatrixShard } from './value-matrix-gate';
|
|
12
|
+
|
|
13
|
+
describeValueMatrixShard('branch', 'phrases');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value matrix gate — the `branch` cells. See value-matrix.ts for the matrix and
|
|
3
|
+
* value-matrix-gate.ts for the assertions; one position per file so the
|
|
4
|
+
* shards run in parallel.
|
|
5
|
+
*
|
|
6
|
+
* @vitest-environment node
|
|
7
|
+
* Required, not a preference: under happy-dom the engines bind happy-dom's
|
|
8
|
+
* DOM constructors, not the jsdom window's (see
|
|
9
|
+
* shipped-examples-execution.test.ts).
|
|
10
|
+
*/
|
|
11
|
+
import { describeValueMatrixShard } from './value-matrix-gate';
|
|
12
|
+
|
|
13
|
+
describeValueMatrixShard('branch');
|
|
@@ -9,16 +9,17 @@
|
|
|
9
9
|
*
|
|
10
10
|
* This does. A CELL is one value expression in one position:
|
|
11
11
|
*
|
|
12
|
-
* - OPERANDS:
|
|
12
|
+
* - OPERANDS: twelve kinds (literal, variable, selector, possessive, `of`,
|
|
13
13
|
* dotted, call, array, parens, a reference's property: `event's type`,
|
|
14
|
-
* `the id of target`,
|
|
15
|
-
* of the value types it produces;
|
|
14
|
+
* `the id of target`, a sigil variable: `$n`, and a CSS length: `100px`),
|
|
15
|
+
* each with instances of the value types it produces;
|
|
16
16
|
* - OPERATORS: arithmetic, comparison, equality, logic, membership, and the
|
|
17
17
|
* prefix and postfix forms (`not`, `-`, `no`, `is empty`, `is null`,
|
|
18
18
|
* `exists`, `as`), and core's operator PHRASES (`is greater than or equal
|
|
19
19
|
* to`, `does not include`, `is an Element`, …);
|
|
20
|
-
* - POSITIONS: a `put` value, a `set` value, an `if` condition,
|
|
21
|
-
*
|
|
20
|
+
* - POSITIONS: a `put` value, a `set` value, an `if` condition, an `if`
|
|
21
|
+
* condition with no `then` before a command that names no target
|
|
22
|
+
* (`branch`), a `repeat while` condition, a loop's count (`repeat … times`), an `increment …
|
|
22
23
|
* by` amount, a `set` value in the second command of a chain (`chain`),
|
|
23
24
|
* what a `get` reads (`get`), and two written targets: what a `set`
|
|
24
25
|
* writes (`assign`) and what an `increment` counts (`count`).
|
|
@@ -112,15 +113,27 @@ export type OperandKind =
|
|
|
112
113
|
| 'array'
|
|
113
114
|
| 'parens'
|
|
114
115
|
| 'reference'
|
|
115
|
-
| 'sigil'
|
|
116
|
+
| 'sigil'
|
|
117
|
+
| 'length';
|
|
116
118
|
|
|
117
119
|
export type Position =
|
|
118
|
-
|
|
120
|
+
| 'put'
|
|
121
|
+
| 'set'
|
|
122
|
+
| 'if'
|
|
123
|
+
| 'branch'
|
|
124
|
+
| 'while'
|
|
125
|
+
| 'times'
|
|
126
|
+
| 'increment'
|
|
127
|
+
| 'chain'
|
|
128
|
+
| 'get'
|
|
129
|
+
| 'assign'
|
|
130
|
+
| 'count';
|
|
119
131
|
|
|
120
132
|
export const POSITIONS: readonly Position[] = [
|
|
121
133
|
'put',
|
|
122
134
|
'set',
|
|
123
135
|
'if',
|
|
136
|
+
'branch',
|
|
124
137
|
'while',
|
|
125
138
|
'times',
|
|
126
139
|
'increment',
|
|
@@ -164,7 +177,7 @@ export interface MatrixCell {
|
|
|
164
177
|
*/
|
|
165
178
|
export const FIXTURE =
|
|
166
179
|
'<div id="out">∅</div>' +
|
|
167
|
-
'<p id="a" class="x" title="t1">6</p>' +
|
|
180
|
+
'<p id="a" class="x" title="t1" aria-pressed="true">6</p>' +
|
|
168
181
|
'<div id="w"><p class="w">w</p><p class="w">v</p></div>' +
|
|
169
182
|
'<button id="b">b</button>';
|
|
170
183
|
|
|
@@ -197,12 +210,16 @@ export const OPERANDS: readonly Operand[] = [
|
|
|
197
210
|
{ kind: 'possessive', type: 'num', text: "obj's v" },
|
|
198
211
|
{ kind: 'possessive', type: 'num', text: "arr's length" },
|
|
199
212
|
{ kind: 'possessive', type: 'num', text: "#a's textContent's length" },
|
|
213
|
+
// A property name a lexicon translates and no reader brings back: es wrote
|
|
214
|
+
// `hijos`, which read back as a property named `hijos`.
|
|
215
|
+
{ kind: 'possessive', type: 'num', text: "#w's children's length" },
|
|
200
216
|
{ kind: 'of', type: 'nstr', text: 'textContent of #a' },
|
|
201
217
|
{ kind: 'of', type: 'nstr', text: 'the textContent of #a' },
|
|
202
218
|
{ kind: 'of', type: 'num', text: 'v of obj' },
|
|
203
219
|
{ kind: 'of', type: 'num', text: 'length of arr' },
|
|
204
220
|
{ kind: 'of', type: 'str', text: '@title of #a' },
|
|
205
221
|
{ kind: 'of', type: 'num', text: 'v of w of obj' },
|
|
222
|
+
{ kind: 'of', type: 'num', text: 'the length of the children of #w' },
|
|
206
223
|
{ kind: 'of', type: 'arr', text: 'textContent of .w' },
|
|
207
224
|
{ kind: 'dotted', type: 'nstr', text: '#a.textContent' },
|
|
208
225
|
{ kind: 'dotted', type: 'str', text: 'me.id' },
|
|
@@ -213,8 +230,15 @@ export const OPERANDS: readonly Operand[] = [
|
|
|
213
230
|
{ kind: 'call', type: 'nstr', text: 'String(n)' },
|
|
214
231
|
{ kind: 'call', type: 'str', text: 's.toUpperCase()' },
|
|
215
232
|
{ kind: 'call', type: 'bool', text: 'Array.isArray(arr)' },
|
|
233
|
+
// A word a lexicon translates, inside a call's arguments, which every reader
|
|
234
|
+
// takes as written: es `Math.max(n, verdadero)`.
|
|
235
|
+
{ kind: 'call', type: 'num', text: 'Math.max(n, true)' },
|
|
216
236
|
{ kind: 'array', type: 'arr', text: '[1, 2]' },
|
|
217
237
|
{ kind: 'array', type: 'arr', text: '[n, 2]' },
|
|
238
|
+
// A word some language's lexicon translates, inside an array in a larger
|
|
239
|
+
// expression: every reader takes a bracket group whole, so a renderer that
|
|
240
|
+
// localized `true` there (es `2 is in [verdadero, n]`) lost it in all 23.
|
|
241
|
+
{ kind: 'array', type: 'arr', text: '[true, n]' },
|
|
218
242
|
{ kind: 'parens', type: 'num', text: '(n + 1)' },
|
|
219
243
|
{ kind: 'parens', type: 'str', text: '(s + "c")' },
|
|
220
244
|
{ kind: 'parens', type: 'bool', text: '(n > 1)' },
|
|
@@ -235,6 +259,10 @@ export const OPERANDS: readonly Operand[] = [
|
|
|
235
259
|
// a variable, but no cell had one: semantic's English dropped a whole loop
|
|
236
260
|
// counted by one (`repeat $n times`), and every translation with it.
|
|
237
261
|
{ kind: 'sigil', type: 'num', text: '$n' },
|
|
262
|
+
// A number with a CSS unit is one string upstream (`100px`): every reader
|
|
263
|
+
// split it, so `set my *width to 100px` was refused in all 24 languages.
|
|
264
|
+
{ kind: 'length', type: 'str', text: '100px' },
|
|
265
|
+
{ kind: 'length', type: 'str', text: '50%' },
|
|
238
266
|
];
|
|
239
267
|
|
|
240
268
|
/** An operator slot, and the value types that fill it. */
|
|
@@ -330,7 +358,16 @@ export const BINARY_OPERATORS: readonly BinaryOperator[] = [
|
|
|
330
358
|
representative: true,
|
|
331
359
|
signatures: [sig('num', 'arr', '2', '[1, 2, 6]', 'bool')],
|
|
332
360
|
},
|
|
333
|
-
{
|
|
361
|
+
{
|
|
362
|
+
op: 'matches',
|
|
363
|
+
representative: true,
|
|
364
|
+
// An attribute selector whose value a lexicon translates: es wrote
|
|
365
|
+
// `<[aria-pressed=verdadero]/>`, another selector, in all 23 languages.
|
|
366
|
+
signatures: [
|
|
367
|
+
sig('one', 'none', '#a', '.x', 'bool'),
|
|
368
|
+
sig('one', 'none', '#a', '<[aria-pressed=true]/>', 'bool'),
|
|
369
|
+
],
|
|
370
|
+
},
|
|
334
371
|
// Operator PHRASES (the after-85 handoff, part 2): the words each language
|
|
335
372
|
// renders for them are read back by the join and the sense table, which no
|
|
336
373
|
// single-word operator exercises. One of each class is representative, so the
|
|
@@ -623,6 +660,7 @@ const TEMPLATES: Record<Position, (expression: string) => string> = {
|
|
|
623
660
|
put: e => `on click put ${e} into #out`,
|
|
624
661
|
set: e => `on click set x to ${e} then put x into #out`,
|
|
625
662
|
if: e => `on click if ${e} then put "Y" into #out else put "N" into #out end`,
|
|
663
|
+
branch: e => `on click if ${e} add .y else add .n end then put my className into #out`,
|
|
626
664
|
while: e => `on click set i to 0 then repeat while i < ${e} increment i end then put i into #out`,
|
|
627
665
|
times: e => `on click set i to 0 then repeat ${e} times increment i end then put i into #out`,
|
|
628
666
|
increment: e => `on click set i to 1 then increment i by ${e} then put i into #out`,
|
|
@@ -649,10 +687,18 @@ const TEMPLATES: Record<Position, (expression: string) => string> = {
|
|
|
649
687
|
* `it`, but its role took no literal, so English dropped `get "hello"`, `get
|
|
650
688
|
* 3` and `get true` whole (and every translation with them) while `get n` and
|
|
651
689
|
* `get #a's textContent` read.
|
|
690
|
+
*
|
|
691
|
+
* A `branch` condition is every `if` condition but the names (none failed
|
|
692
|
+
* there that `if` did not). The `if` cells write `then` and a `put` with its
|
|
693
|
+
* target, so a reader always saw where the condition ended. Without `then`, an
|
|
694
|
+
* SOV language writes the branch's first role right after the condition, and
|
|
695
|
+
* its reader has to find the seam: tr read `if 2 is in arr add .a end` as
|
|
696
|
+
* `if 2 add .a to is in arr end` (its destination marker is optional, and `in`
|
|
697
|
+
* is also one), with every operand, and the `if` cells all passed.
|
|
652
698
|
*/
|
|
653
699
|
function positionsFor(type: ValueType, bare: boolean, group: MatrixCell['group']): Position[] {
|
|
654
700
|
const out: Position[] = ['put', 'set'];
|
|
655
|
-
if (type === 'bool' || bare) out.push('if');
|
|
701
|
+
if (type === 'bool' || bare) out.push('if', 'branch');
|
|
656
702
|
if (type === 'num' || type === 'nstr') out.push('while', 'times', 'increment');
|
|
657
703
|
if (group !== 'operand-operator') out.push('chain', 'get');
|
|
658
704
|
return out;
|