xtl-py 0.1.0a0__tar.gz
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.
- xtl_py-0.1.0a0/.gitignore +10 -0
- xtl_py-0.1.0a0/.python-version +1 -0
- xtl_py-0.1.0a0/PKG-INFO +10 -0
- xtl_py-0.1.0a0/PORTING_NOTES.md +160 -0
- xtl_py-0.1.0a0/README.md +0 -0
- xtl_py-0.1.0a0/conformance-report.json +485 -0
- xtl_py-0.1.0a0/pyproject.toml +50 -0
- xtl_py-0.1.0a0/src/xl3/__init__.py +78 -0
- xtl_py-0.1.0a0/src/xl3/directives.py +391 -0
- xtl_py-0.1.0a0/src/xl3/errors.py +88 -0
- xtl_py-0.1.0a0/src/xl3/evaluator.py +366 -0
- xtl_py-0.1.0a0/src/xl3/expression.py +478 -0
- xtl_py-0.1.0a0/src/xl3/filename.py +65 -0
- xtl_py-0.1.0a0/src/xl3/functions.py +245 -0
- xtl_py-0.1.0a0/src/xl3/inputs.py +107 -0
- xtl_py-0.1.0a0/src/xl3/parser.py +505 -0
- xtl_py-0.1.0a0/src/xl3/pipeline.py +51 -0
- xtl_py-0.1.0a0/src/xl3/py.typed +0 -0
- xtl_py-0.1.0a0/src/xl3/reader.py +350 -0
- xtl_py-0.1.0a0/src/xl3/renderer.py +809 -0
- xtl_py-0.1.0a0/src/xl3/runner/__init__.py +1 -0
- xtl_py-0.1.0a0/src/xl3/runner/__main__.py +246 -0
- xtl_py-0.1.0a0/src/xl3/runner/compare.py +135 -0
- xtl_py-0.1.0a0/src/xl3/runner/discover.py +188 -0
- xtl_py-0.1.0a0/src/xl3/runner/dynamic.py +105 -0
- xtl_py-0.1.0a0/src/xl3/types.py +79 -0
- xtl_py-0.1.0a0/src/xl3/value_model.py +315 -0
- xtl_py-0.1.0a0/tests/__init__.py +0 -0
- xtl_py-0.1.0a0/tests/unit/__init__.py +0 -0
- xtl_py-0.1.0a0/tests/unit/test_value_model.py +263 -0
- xtl_py-0.1.0a0/uv.lock +398 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
xtl_py-0.1.0a0/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xtl-py
|
|
3
|
+
Version: 0.1.0a0
|
|
4
|
+
Summary: XTL (Excel Template Language) 0.1 — Python reference implementation
|
|
5
|
+
Project-URL: Spec, https://github.com/jinyoung4478/xl3
|
|
6
|
+
Author-email: jinyoung4478 <skswls0@daum.net>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Requires-Dist: openpyxl<4,>=3.1
|
|
10
|
+
Requires-Dist: pyyaml<7,>=6
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Porting Notes — XTL TS → Python
|
|
2
|
+
|
|
3
|
+
A running log of points where the spec is genuinely underspecified,
|
|
4
|
+
internally inconsistent, or where the Python port had to make a non-obvious
|
|
5
|
+
call. Format per entry:
|
|
6
|
+
|
|
7
|
+
- **Where**: spec section / ADR / fixture number
|
|
8
|
+
- **Question**: the ambiguity in one sentence
|
|
9
|
+
- **TS impl behavior**: what the JS reference implementation does today
|
|
10
|
+
- **Other reasonable interpretations**: alternatives we considered
|
|
11
|
+
- **Our choice**: what the Python port does, and why
|
|
12
|
+
- **Severity**: spec/impl/test (does it block conformance? does it block
|
|
13
|
+
cross-impl portability?)
|
|
14
|
+
|
|
15
|
+
When this file accumulates 5+ entries, batch them into an issue against
|
|
16
|
+
`xl3` and propose ADR amendments.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## #1. ECMA-262 scientific-notation cutoff is `1e-6`, not `1e-4`
|
|
21
|
+
|
|
22
|
+
**Where**: `spec/language.md` "Canonical String Form"; `spec/decisions/0009-comparison-and-string-coercion.md` §"Canonical string form".
|
|
23
|
+
|
|
24
|
+
**Question**: ADR-0009 says the canonical-string form of a finite number uses
|
|
25
|
+
"no scientific notation for magnitudes between `1e-4` and `1e21`," and claims
|
|
26
|
+
this matches `Number.prototype.toString`. But ECMA-262 §6.1.6.1.13 actually
|
|
27
|
+
uses a **`-6`** cutoff, not `-4`. So `(0.00005).toString()` is `"0.00005"` in
|
|
28
|
+
JS — decimal, not scientific. The spec text and the cited authority disagree.
|
|
29
|
+
|
|
30
|
+
**TS impl behavior**: uses the host `Number.prototype.toString`, so follows
|
|
31
|
+
ECMA-262 (`-6` cutoff). The fixture corpus was authored against this
|
|
32
|
+
behavior.
|
|
33
|
+
|
|
34
|
+
**Other reasonable interpretations**:
|
|
35
|
+
- Take the spec text literally: scientific for `0 < |x| < 1e-4`. Diverges
|
|
36
|
+
from JS impl + corpus.
|
|
37
|
+
- Take ECMA-262 literally: `-6` cutoff. Matches impl + corpus, contradicts
|
|
38
|
+
spec text.
|
|
39
|
+
|
|
40
|
+
**Our choice**: ECMA-262 cutoff (`-6`). Implementing the literal spec
|
|
41
|
+
text would fail any fixture exercising values in `[1e-6, 1e-4)`. Our
|
|
42
|
+
`canonical_number` re-implements ECMA-262 §6.1.6.1.13 directly so the
|
|
43
|
+
behavior is JS-compatible regardless of host language quirks.
|
|
44
|
+
|
|
45
|
+
**Severity**: spec — propose an ADR amendment that replaces "1e-4" with
|
|
46
|
+
"1e-6" and either drops the redundant `Number.prototype.toString` cite or
|
|
47
|
+
keeps it now that the text matches.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## #2. ECMA `String.prototype.trim` historically includes U+FEFF; ADR-0007 excludes it
|
|
52
|
+
|
|
53
|
+
**Where**: `spec/decisions/0007-empty-value-definition.md`.
|
|
54
|
+
|
|
55
|
+
**Question**: ADR-0007 says whitespace "matches the set recognized by
|
|
56
|
+
ECMAScript `String.prototype.trim` — equivalent to the Unicode-mode `\s`
|
|
57
|
+
character class," but then explicitly excludes U+FEFF (zero-width no-break
|
|
58
|
+
space / BOM) and other zero-width characters. ECMAScript's `WhiteSpace`
|
|
59
|
+
production has historically *included* U+FEFF, and most engines implement
|
|
60
|
+
`trim()` accordingly. So a string of bare U+FEFF is empty per native JS
|
|
61
|
+
trim but **non-empty** per ADR-0007.
|
|
62
|
+
|
|
63
|
+
**TS impl behavior**: uses native `String.prototype.trim`, which strips
|
|
64
|
+
U+FEFF on V8/JSC/SM. So a bare-U+FEFF source cell is empty per the impl,
|
|
65
|
+
non-empty per the spec.
|
|
66
|
+
|
|
67
|
+
**Other reasonable interpretations**:
|
|
68
|
+
- Follow Unicode `White_Space` property strictly (what the ADR text says).
|
|
69
|
+
- Follow ECMA-262 `WhiteSpace` production (what the impl actually does).
|
|
70
|
+
|
|
71
|
+
**Our choice**: Unicode `White_Space` (Python `str.isspace()`), per the
|
|
72
|
+
ADR's normative bullet. No fixture currently asserts this edge case, so the
|
|
73
|
+
divergence is silent for the bootstrap corpus, but we follow the spec.
|
|
74
|
+
|
|
75
|
+
**Severity**: impl — TS impl deviates from spec on a technicality. Worth a
|
|
76
|
+
fixture (`empty-zwnbsp-not-whitespace`) to pin behavior either way.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## #3. Python `repr(float)` exponent padding differs from ECMA `Number.prototype.toString`
|
|
81
|
+
|
|
82
|
+
**Where**: implementation detail; affects every numeric `&` concat and every
|
|
83
|
+
single-expression cell coercion fallback.
|
|
84
|
+
|
|
85
|
+
**Question**: Python `repr(1e-7)` is `"1e-07"` (two-digit padded exponent);
|
|
86
|
+
JS `(1e-7).toString()` is `"1e-7"`. Python `repr(-0.0)` is `"-0.0"`; JS
|
|
87
|
+
`(-0).toString()` is `"0"`.
|
|
88
|
+
|
|
89
|
+
**TS impl behavior**: matches ECMA-262 by virtue of being JS.
|
|
90
|
+
|
|
91
|
+
**Our choice**: re-implement ECMA-262 §6.1.6.1.13 directly in
|
|
92
|
+
`canonical_number`. Don't trust `repr(float)` to format identically — we
|
|
93
|
+
extract digits + decimal exponent from `repr` and re-render using the
|
|
94
|
+
ECMA format selection rules (decimal in [1e-6, 1e21), scientific outside).
|
|
95
|
+
Negative zero returns `"0"`.
|
|
96
|
+
|
|
97
|
+
**Severity**: impl — just a thing the port has to handle.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## #4. Excel serial dates are timezone-naive; ADR-0017 mandates UTC
|
|
102
|
+
|
|
103
|
+
**Where**: ADR-0017 §"Timezone (normative)".
|
|
104
|
+
|
|
105
|
+
**Question**: ADR-0017 says Date components MUST be read in UTC.
|
|
106
|
+
ExcelJS exposes Excel's timezone-naive serial dates as `Date` objects
|
|
107
|
+
*anchored at UTC midnight*, and the impl uses `getUTCFullYear` etc.
|
|
108
|
+
openpyxl returns Python `datetime.datetime` objects with `tzinfo=None`
|
|
109
|
+
(naive); calling `.year`/`.month`/`.date()` on them yields the serial's
|
|
110
|
+
naked components, which is what ADR-0017 wants — but only if you don't
|
|
111
|
+
"localize" them first.
|
|
112
|
+
|
|
113
|
+
**TS impl behavior**: explicit `getUTC*` calls.
|
|
114
|
+
|
|
115
|
+
**Our choice**: never call `.astimezone()` on a naive datetime; treat
|
|
116
|
+
naive openpyxl datetimes as already-UTC. `canonical_date` strips
|
|
117
|
+
`tzinfo` defensively if present.
|
|
118
|
+
|
|
119
|
+
**Severity**: impl — easy to get wrong subtly. Worth a Stage 1 timezone
|
|
120
|
+
matrix fixture run (CI matrix per `STABILITY.md`).
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## #5. TS IF-condition normalizer recognizes `==` but NOT `=` — spec uses `=`
|
|
125
|
+
|
|
126
|
+
**Where**: `xl3/src/normalizer.ts` lines ~215-225; fixture
|
|
127
|
+
`048-if-and-comparison-boundaries`.
|
|
128
|
+
|
|
129
|
+
**Question**: `language.md` §"Comparison Operators" lists `=` (single equal)
|
|
130
|
+
as the equality operator. The TS normalizer's IF-condition op list is
|
|
131
|
+
`[['!=', 'ne'], ['>=', 'ge'], ['<=', 'le'], ['==', 'eq'], ['>', 'gt'],
|
|
132
|
+
['<', 'lt']]` — it has `==` but NOT `=`. So
|
|
133
|
+
`IF([Amount] = 0, "zero", "non-zero")` is normalized as a function call
|
|
134
|
+
where the condition is the *raw string* `"[Amount] = 0"`. At eval time
|
|
135
|
+
it falls through to "bare string literal" which is non-empty → truthy →
|
|
136
|
+
IF always takes the THEN branch regardless of Amount.
|
|
137
|
+
|
|
138
|
+
Fixture 048 was authored against this buggy behavior. Its
|
|
139
|
+
`expected.xlsx` has `D3='zero'` for an Amount=1 row, even though
|
|
140
|
+
spec-correct evaluation of `IF(1 = 0, "zero", "non-zero")` is
|
|
141
|
+
`"non-zero"`.
|
|
142
|
+
|
|
143
|
+
**TS impl behavior**: bug — `=` not recognized in IF condition; treated
|
|
144
|
+
as literal string (always truthy).
|
|
145
|
+
|
|
146
|
+
**Other reasonable interpretations**: this is unambiguously a TS impl
|
|
147
|
+
bug under the spec.
|
|
148
|
+
|
|
149
|
+
**Our choice**: follow the spec — `=` is the equality operator
|
|
150
|
+
everywhere, including IF conditions. We FAIL fixture 048.
|
|
151
|
+
|
|
152
|
+
**Severity**: impl + test — `xl3/src/normalizer.ts` should add `=` to
|
|
153
|
+
the ops list before `==`; and fixture 048's `expected.xlsx` should be
|
|
154
|
+
re-authored to reflect spec-correct evaluation. Note that `@filter [col]
|
|
155
|
+
= value` works correctly in TS (different parser). Other fixtures using
|
|
156
|
+
`=` (063, 064, 079, 080, 081, 088, 092) are inside @filter / @join
|
|
157
|
+
clauses that TS parses with a separate code path that does handle `=`.
|
|
158
|
+
Fixture 048 is the only one currently affected.
|
|
159
|
+
|
|
160
|
+
## #6. (placeholder — append future ambiguities below)
|
xtl_py-0.1.0a0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
{
|
|
2
|
+
"implementation": "xl3-py",
|
|
3
|
+
"version": "0.1.0a0",
|
|
4
|
+
"spec_version": "0.1",
|
|
5
|
+
"comparison_stage": 1,
|
|
6
|
+
"results": [
|
|
7
|
+
{
|
|
8
|
+
"fixture": "001-bracket-substitution",
|
|
9
|
+
"status": "pass",
|
|
10
|
+
"duration_ms": 12
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"fixture": "002-if-function",
|
|
14
|
+
"status": "pass",
|
|
15
|
+
"duration_ms": 5
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"fixture": "003-list-sheet-filter",
|
|
19
|
+
"status": "pass",
|
|
20
|
+
"duration_ms": 5
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"fixture": "004-repeat-right-default",
|
|
24
|
+
"status": "pass",
|
|
25
|
+
"duration_ms": 5
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"fixture": "005-round-half-away-from-zero",
|
|
29
|
+
"status": "pass",
|
|
30
|
+
"duration_ms": 5
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"fixture": "006-filename-forbidden-chars",
|
|
34
|
+
"status": "pass",
|
|
35
|
+
"duration_ms": 4
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"fixture": "007-filename-reserved-name",
|
|
39
|
+
"status": "pass",
|
|
40
|
+
"duration_ms": 4
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"fixture": "008-numfmt-numeric-string-coercion",
|
|
44
|
+
"status": "pass",
|
|
45
|
+
"duration_ms": 4
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"fixture": "009-numfmt-date-string-coercion",
|
|
49
|
+
"status": "pass",
|
|
50
|
+
"duration_ms": 4
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"fixture": "010-numfmt-text-format-coercion",
|
|
54
|
+
"status": "pass",
|
|
55
|
+
"duration_ms": 4
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"fixture": "011-text-date-format",
|
|
59
|
+
"status": "pass",
|
|
60
|
+
"duration_ms": 4
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"fixture": "012-text-number-format",
|
|
64
|
+
"status": "pass",
|
|
65
|
+
"duration_ms": 5
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"fixture": "013-rich-text-template-expression",
|
|
69
|
+
"status": "pass",
|
|
70
|
+
"duration_ms": 5
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"fixture": "014-source-formula-cached-result",
|
|
74
|
+
"status": "pass",
|
|
75
|
+
"duration_ms": 5
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"fixture": "015-source-sheet-prefix-first-match",
|
|
79
|
+
"status": "pass",
|
|
80
|
+
"duration_ms": 5
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"fixture": "016-text-number-negative-rounding",
|
|
84
|
+
"status": "pass",
|
|
85
|
+
"duration_ms": 5
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"fixture": "017-source-sheet-prefix-no-match-error",
|
|
89
|
+
"status": "pass",
|
|
90
|
+
"duration_ms": 2
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"fixture": "018-source-formula-missing-cached-result-error",
|
|
94
|
+
"status": "pass",
|
|
95
|
+
"duration_ms": 2
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"fixture": "019-filename-empty-basename-error",
|
|
99
|
+
"status": "pass",
|
|
100
|
+
"duration_ms": 5
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"fixture": "020-filename-length-overflow-error",
|
|
104
|
+
"status": "pass",
|
|
105
|
+
"duration_ms": 8
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"fixture": "021-numfmt-number-coercion-error",
|
|
109
|
+
"status": "pass",
|
|
110
|
+
"duration_ms": 3
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"fixture": "022-numfmt-date-coercion-error",
|
|
114
|
+
"status": "pass",
|
|
115
|
+
"duration_ms": 3
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"fixture": "023-today-utc-dynamic",
|
|
119
|
+
"status": "pass",
|
|
120
|
+
"duration_ms": 4
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"fixture": "024-stage2-merge-preservation",
|
|
124
|
+
"status": "skip",
|
|
125
|
+
"reason": "requires comparison_stage 2"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"fixture": "025-stage2-style-numfmt-preservation",
|
|
129
|
+
"status": "skip",
|
|
130
|
+
"reason": "requires comparison_stage 2"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"fixture": "026-stage2-splice-merge-style-preservation",
|
|
134
|
+
"status": "skip",
|
|
135
|
+
"reason": "requires comparison_stage 2"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"fixture": "027-stage2-cross-writer-canonicalization",
|
|
139
|
+
"status": "skip",
|
|
140
|
+
"reason": "requires comparison_stage 2"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"fixture": "028-source-table-row-shorthand",
|
|
144
|
+
"status": "pass",
|
|
145
|
+
"duration_ms": 5
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"fixture": "029-source-table-open-range",
|
|
149
|
+
"status": "pass",
|
|
150
|
+
"duration_ms": 5
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"fixture": "030-source-table-finite-range",
|
|
154
|
+
"status": "pass",
|
|
155
|
+
"duration_ms": 5
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"fixture": "031-source-table-zero-data-range",
|
|
159
|
+
"status": "pass",
|
|
160
|
+
"duration_ms": 3
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"fixture": "032-source-table-empty-column-name-error",
|
|
164
|
+
"status": "pass",
|
|
165
|
+
"duration_ms": 2
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"fixture": "033-source-table-duplicate-column-name-error",
|
|
169
|
+
"status": "pass",
|
|
170
|
+
"duration_ms": 2
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"fixture": "034-source-table-invalid-selector-error",
|
|
174
|
+
"status": "pass",
|
|
175
|
+
"duration_ms": 2
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"fixture": "035-source-table-rich-text-header",
|
|
179
|
+
"status": "pass",
|
|
180
|
+
"duration_ms": 5
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"fixture": "036-source-table-formula-header",
|
|
184
|
+
"status": "pass",
|
|
185
|
+
"duration_ms": 5
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"fixture": "037-source-table-formula-header-missing-cache-error",
|
|
189
|
+
"status": "pass",
|
|
190
|
+
"duration_ms": 2
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"fixture": "038-source-sheet-exact-match-beats-prefix",
|
|
194
|
+
"status": "pass",
|
|
195
|
+
"duration_ms": 5
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"fixture": "039-source-sheet-default-first-worksheet",
|
|
199
|
+
"status": "pass",
|
|
200
|
+
"duration_ms": 5
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"fixture": "040-list-sheet-hidden-states-removed",
|
|
204
|
+
"status": "pass",
|
|
205
|
+
"duration_ms": 6
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"fixture": "041-row-function-inside-repeat-block",
|
|
209
|
+
"status": "pass",
|
|
210
|
+
"duration_ms": 5
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"fixture": "042-row-function-outside-repeat-block-error",
|
|
214
|
+
"status": "pass",
|
|
215
|
+
"duration_ms": 3
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"fixture": "043-ifempty-function",
|
|
219
|
+
"status": "pass",
|
|
220
|
+
"duration_ms": 5
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"fixture": "044-sort-and-top-order",
|
|
224
|
+
"status": "pass",
|
|
225
|
+
"duration_ms": 5
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"fixture": "045-list-sheet-not-in-filter",
|
|
229
|
+
"status": "pass",
|
|
230
|
+
"duration_ms": 5
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"fixture": "046-count-field-non-empty",
|
|
234
|
+
"status": "pass",
|
|
235
|
+
"duration_ms": 5
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"fixture": "047-aggregate-functions",
|
|
239
|
+
"status": "pass",
|
|
240
|
+
"duration_ms": 6
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"fixture": "048-if-and-comparison-boundaries",
|
|
244
|
+
"status": "fail",
|
|
245
|
+
"duration_ms": 6,
|
|
246
|
+
"diff": "Report!D3: expected 'zero', got 'non-zero'"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"fixture": "049-filename-sanitization-warning",
|
|
250
|
+
"status": "pass",
|
|
251
|
+
"duration_ms": 5
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"fixture": "050-empty-ifempty-whitespace-only",
|
|
255
|
+
"status": "pass",
|
|
256
|
+
"duration_ms": 5
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"fixture": "051-empty-ifempty-zero-not-empty",
|
|
260
|
+
"status": "pass",
|
|
261
|
+
"duration_ms": 5
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"fixture": "052-empty-count-field-whitespace-zero-false",
|
|
265
|
+
"status": "pass",
|
|
266
|
+
"duration_ms": 6
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"fixture": "053-empty-row-skip-whitespace-only",
|
|
270
|
+
"status": "pass",
|
|
271
|
+
"duration_ms": 5
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"fixture": "054-empty-list-membership",
|
|
275
|
+
"status": "pass",
|
|
276
|
+
"duration_ms": 5
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"fixture": "055-if-truthy-zero-and-empty",
|
|
280
|
+
"status": "pass",
|
|
281
|
+
"duration_ms": 5
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"fixture": "056-if-truthy-string-zero-not-special",
|
|
285
|
+
"status": "pass",
|
|
286
|
+
"duration_ms": 5
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"fixture": "057-if-truthy-boolean",
|
|
290
|
+
"status": "pass",
|
|
291
|
+
"duration_ms": 5
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"fixture": "058-if-comparison-result",
|
|
295
|
+
"status": "pass",
|
|
296
|
+
"duration_ms": 5
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"fixture": "059-compare-numeric-string-vs-number",
|
|
300
|
+
"status": "pass",
|
|
301
|
+
"duration_ms": 5
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"fixture": "060-compare-string-codepoint-order",
|
|
305
|
+
"status": "pass",
|
|
306
|
+
"duration_ms": 5
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"fixture": "061-concat-canonical-form",
|
|
310
|
+
"status": "pass",
|
|
311
|
+
"duration_ms": 5
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"fixture": "062-concat-empty-stringifies-to-empty",
|
|
315
|
+
"status": "pass",
|
|
316
|
+
"duration_ms": 5
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"fixture": "063-compare-empty-vs-value",
|
|
320
|
+
"status": "pass",
|
|
321
|
+
"duration_ms": 5
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"fixture": "064-compare-unicode-minus-not-numeric",
|
|
325
|
+
"status": "pass",
|
|
326
|
+
"duration_ms": 5
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"fixture": "065-input-text-default-applied",
|
|
330
|
+
"status": "pass",
|
|
331
|
+
"duration_ms": 5
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"fixture": "066-input-text-host-supplied",
|
|
335
|
+
"status": "pass",
|
|
336
|
+
"duration_ms": 5
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"fixture": "067-input-missing-required-error",
|
|
340
|
+
"status": "pass",
|
|
341
|
+
"duration_ms": 1
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"fixture": "068-input-select-host-supplied",
|
|
345
|
+
"status": "pass",
|
|
346
|
+
"duration_ms": 5
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"fixture": "069-source-multi-declaration",
|
|
350
|
+
"status": "pass",
|
|
351
|
+
"duration_ms": 5
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"fixture": "070-source-aggregate-cross-source",
|
|
355
|
+
"status": "pass",
|
|
356
|
+
"duration_ms": 6
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"fixture": "071-source-directive-active",
|
|
360
|
+
"status": "pass",
|
|
361
|
+
"duration_ms": 5
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"fixture": "072-source-undeclared-error",
|
|
365
|
+
"status": "pass",
|
|
366
|
+
"duration_ms": 3
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"fixture": "073-source-row-cross-error",
|
|
370
|
+
"status": "pass",
|
|
371
|
+
"duration_ms": 4
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"fixture": "074-xlookup-basic",
|
|
375
|
+
"status": "pass",
|
|
376
|
+
"duration_ms": 5
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"fixture": "075-xlookup-fallback",
|
|
380
|
+
"status": "pass",
|
|
381
|
+
"duration_ms": 5
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"fixture": "076-xlookup-no-match-error",
|
|
385
|
+
"status": "pass",
|
|
386
|
+
"duration_ms": 8
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"fixture": "077-xlookup-source-mismatch-error",
|
|
390
|
+
"status": "pass",
|
|
391
|
+
"duration_ms": 4
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"fixture": "078-xlookup-bare-bracket-error",
|
|
395
|
+
"status": "pass",
|
|
396
|
+
"duration_ms": 4
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"fixture": "079-join-basic-inner",
|
|
400
|
+
"status": "pass",
|
|
401
|
+
"duration_ms": 6
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"fixture": "080-join-no-match-dropped",
|
|
405
|
+
"status": "pass",
|
|
406
|
+
"duration_ms": 5
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"fixture": "081-join-undeclared-source-error",
|
|
410
|
+
"status": "pass",
|
|
411
|
+
"duration_ms": 3
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"fixture": "082-join-bad-on-clause-error",
|
|
415
|
+
"status": "pass",
|
|
416
|
+
"duration_ms": 4
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"fixture": "083-sort-stable-equal-keys",
|
|
420
|
+
"status": "pass",
|
|
421
|
+
"duration_ms": 5
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"fixture": "084-sort-multi-stable-priority",
|
|
425
|
+
"status": "pass",
|
|
426
|
+
"duration_ms": 5
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"fixture": "085-file-group-first-seen-order",
|
|
430
|
+
"status": "pass",
|
|
431
|
+
"duration_ms": 10
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"fixture": "086-sheet-group-first-seen-order",
|
|
435
|
+
"status": "pass",
|
|
436
|
+
"duration_ms": 5
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"fixture": "087-date-canonical-string-concat",
|
|
440
|
+
"status": "pass",
|
|
441
|
+
"duration_ms": 5
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"fixture": "088-date-comparison-equality",
|
|
445
|
+
"status": "pass",
|
|
446
|
+
"duration_ms": 4
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"fixture": "089-error-sentinel-empty",
|
|
450
|
+
"status": "pass",
|
|
451
|
+
"duration_ms": 5
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
"fixture": "090-percentage-numeric-flow",
|
|
455
|
+
"status": "pass",
|
|
456
|
+
"duration_ms": 5
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"fixture": "091-source-unknown-column-error",
|
|
460
|
+
"status": "pass",
|
|
461
|
+
"duration_ms": 4
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
"fixture": "092-composed-multi-source-join-filter-sort",
|
|
465
|
+
"status": "pass",
|
|
466
|
+
"duration_ms": 7
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
"fixture": "093-stage2-excel-authored-expected",
|
|
470
|
+
"status": "skip",
|
|
471
|
+
"reason": "requires comparison_stage 2"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"fixture": "094-reserved-sheet-name-error",
|
|
475
|
+
"status": "pass",
|
|
476
|
+
"duration_ms": 1
|
|
477
|
+
}
|
|
478
|
+
],
|
|
479
|
+
"summary": {
|
|
480
|
+
"total": 89,
|
|
481
|
+
"passed": 88,
|
|
482
|
+
"failed": 1,
|
|
483
|
+
"skipped": 5
|
|
484
|
+
}
|
|
485
|
+
}
|