@invarn/cibuild 2.1.8 → 2.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/cli.cjs +53 -12
- package/dist/src/yaml/steps/structural-facts.d.ts +73 -2
- package/dist/src/yaml/steps/structural-facts.d.ts.map +1 -1
- package/dist/src/yaml/steps/structural-facts.js +0 -0
- package/dist/src/yaml/steps/structural-facts.test.js +89 -2
- package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +2 -1
- package/package.json +1 -1
|
@@ -39,6 +39,12 @@ export interface LayoutNode {
|
|
|
39
39
|
/** Legacy clip hint (parent clips its children). The current emitter writes a
|
|
40
40
|
* constant false; used only for the legacy (no-`unclippedBounds`) fallback. */
|
|
41
41
|
clipsChildren: boolean;
|
|
42
|
+
/** Accessible text content. */
|
|
43
|
+
text?: string;
|
|
44
|
+
/** Fill (background) color, hex. */
|
|
45
|
+
fillColor?: string;
|
|
46
|
+
/** Text (foreground) color, hex. */
|
|
47
|
+
textColor?: string;
|
|
42
48
|
}
|
|
43
49
|
export interface LayoutDump {
|
|
44
50
|
/** The captured canvas / root bounds in px. */
|
|
@@ -152,10 +158,14 @@ export interface StructuralGeometryElement {
|
|
|
152
158
|
rendered: {
|
|
153
159
|
w: number;
|
|
154
160
|
h: number;
|
|
161
|
+
x: number;
|
|
162
|
+
y: number;
|
|
155
163
|
};
|
|
156
164
|
design: {
|
|
157
165
|
w: number | undefined;
|
|
158
166
|
h: number | undefined;
|
|
167
|
+
x?: number;
|
|
168
|
+
y?: number;
|
|
159
169
|
};
|
|
160
170
|
deltas: Array<{
|
|
161
171
|
dimension: 'w' | 'h' | 'x' | 'y';
|
|
@@ -181,7 +191,67 @@ export interface StructuralBlock {
|
|
|
181
191
|
summary: StructuralSummary;
|
|
182
192
|
facts: StructuralFact[];
|
|
183
193
|
geometry?: StructuralGeometry;
|
|
194
|
+
appearance?: StructuralAppearance;
|
|
184
195
|
}
|
|
196
|
+
/** Which reference the appearance facts were diffed against. `previous_render`
|
|
197
|
+
* facts are regressions/drift, NOT design divergences (the no-Figma path). */
|
|
198
|
+
export type AppearanceBaseline = 'figma' | 'previous_render';
|
|
199
|
+
export type AppearanceFact = {
|
|
200
|
+
kind: 'color';
|
|
201
|
+
elementId: string;
|
|
202
|
+
channel: 'fill' | 'text';
|
|
203
|
+
rendered: string;
|
|
204
|
+
reference: string;
|
|
205
|
+
/** CIEDE2000 perceptual distance — measurement only, no significance. */
|
|
206
|
+
deltaE: number;
|
|
207
|
+
} | {
|
|
208
|
+
kind: 'text_mismatch';
|
|
209
|
+
elementId: string;
|
|
210
|
+
rendered: string;
|
|
211
|
+
reference: string;
|
|
212
|
+
} | {
|
|
213
|
+
kind: 'text_missing';
|
|
214
|
+
elementId: string;
|
|
215
|
+
reference: string;
|
|
216
|
+
}
|
|
217
|
+
/** In the reference, not rendered. */
|
|
218
|
+
| {
|
|
219
|
+
kind: 'element_missing';
|
|
220
|
+
elementId: string;
|
|
221
|
+
}
|
|
222
|
+
/** Rendered, not in the reference. */
|
|
223
|
+
| {
|
|
224
|
+
kind: 'element_extra';
|
|
225
|
+
elementId: string;
|
|
226
|
+
};
|
|
227
|
+
export interface StructuralAppearance {
|
|
228
|
+
baseline: AppearanceBaseline;
|
|
229
|
+
facts: AppearanceFact[];
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The reference side of the appearance diff, passed into `buildStructuralBlock`.
|
|
233
|
+
* `elementIds` is the reference element-id set (Figma node ids on the `figma`
|
|
234
|
+
* baseline; the previous render's element ids on `previous_render`). This slice
|
|
235
|
+
* derives presence only; the copy/color slices extend this with reference text /
|
|
236
|
+
* colors keyed by element id.
|
|
237
|
+
*/
|
|
238
|
+
export interface AppearanceReference {
|
|
239
|
+
baseline: AppearanceBaseline;
|
|
240
|
+
elementIds: string[];
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Element-presence facts from the set difference of the rendered element-id set
|
|
244
|
+
* and the reference element-id set, keyed on the stable element id shared with
|
|
245
|
+
* Signal B. An id present in the reference but not rendered is `element_missing`;
|
|
246
|
+
* an id rendered but not in the reference is `element_extra`.
|
|
247
|
+
*
|
|
248
|
+
* Pure and platform-agnostic: it takes two id collections and is order-
|
|
249
|
+
* independent — the result depends only on the two SETS, not on input order or
|
|
250
|
+
* duplicates. Output is sorted (missing before extra, each by id) so callers get
|
|
251
|
+
* a deterministic list. The deriver reports facts only; significance is a later,
|
|
252
|
+
* separately-wired concern.
|
|
253
|
+
*/
|
|
254
|
+
export declare function derivePresenceFacts(renderedIds: Iterable<string>, referenceIds: Iterable<string>): AppearanceFact[];
|
|
185
255
|
/**
|
|
186
256
|
* Assemble the inline `structural` block (ADR-0004): Signal-A facts always, plus
|
|
187
257
|
* the Signal-B geometry block when `referenceGeometry` is supplied. Size (w/h)
|
|
@@ -191,7 +261,7 @@ export interface StructuralBlock {
|
|
|
191
261
|
export declare function buildStructuralBlock(dump: LayoutDump, referenceGeometry?: ReferenceGeometry | null, density?: number, options?: {
|
|
192
262
|
sizeTolerancePx?: number;
|
|
193
263
|
overhangTolerancePx?: number;
|
|
194
|
-
}): StructuralBlock;
|
|
264
|
+
}, appearanceReference?: AppearanceReference | null): StructuralBlock;
|
|
195
265
|
export interface ReviewRow {
|
|
196
266
|
element: string;
|
|
197
267
|
rendered: string | null;
|
|
@@ -261,10 +331,11 @@ export declare function getGeometryStageInternals(): {
|
|
|
261
331
|
buildStructuralBlock: (dump: LayoutDump, referenceGeometry?: ReferenceGeometry | null, density?: number, options?: {
|
|
262
332
|
sizeTolerancePx?: number;
|
|
263
333
|
overhangTolerancePx?: number;
|
|
264
|
-
}) => StructuralBlock;
|
|
334
|
+
}, appearanceReference?: AppearanceReference | null) => StructuralBlock;
|
|
265
335
|
buildReviewScaffold: (structural: {
|
|
266
336
|
facts?: StructuralFact[];
|
|
267
337
|
geometry?: StructuralGeometry | null;
|
|
268
338
|
}) => ReviewScaffold;
|
|
339
|
+
derivePresenceFacts: (renderedIds: Iterable<string>, referenceIds: Iterable<string>) => AppearanceFact[];
|
|
269
340
|
};
|
|
270
341
|
//# sourceMappingURL=structural-facts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structural-facts.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/structural-facts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX;6CACyC;IACzC,MAAM,EAAE,YAAY,CAAC;IACrB;;;+EAG2E;IAC3E,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;oFACgF;IAChF,aAAa,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"structural-facts.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/structural-facts.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* overflowing voucher tag; the deriver must surface the bottom clip as a
|
|
9
9
|
* fact so it is never again skipped by eye.
|
|
10
10
|
*/
|
|
11
|
-
import { deriveStructuralFacts, diffKeyGeometry, buildStructuralBlock, buildReviewScaffold, getStructuralFactsStageInternals, getGeometryStageInternals, } from './structural-facts.js';
|
|
11
|
+
import { deriveStructuralFacts, derivePresenceFacts, diffKeyGeometry, buildStructuralBlock, buildReviewScaffold, getStructuralFactsStageInternals, getGeometryStageInternals, } from './structural-facts.js';
|
|
12
12
|
// 200x400 canvas in px for all fixtures unless noted.
|
|
13
13
|
const CANVAS = { left: 0, top: 0, right: 200, bottom: 400 };
|
|
14
14
|
describe('deriveStructuralFacts — containment', () => {
|
|
@@ -304,7 +304,9 @@ describe('buildStructuralBlock — with reference geometry (Signal B)', () => {
|
|
|
304
304
|
const voucher = block.geometry?.elements.find((e) => e.id === 'voucher');
|
|
305
305
|
expect(voucher).toEqual({
|
|
306
306
|
id: 'voucher',
|
|
307
|
-
rendered
|
|
307
|
+
// rendered now carries x/y (dp) for the clip reference; design stays
|
|
308
|
+
// w/h-only here (the reference declared no x/y).
|
|
309
|
+
rendered: { w: 96, h: 96, x: 248, y: 0 },
|
|
308
310
|
design: { w: 81, h: 81 },
|
|
309
311
|
deltas: [{ dimension: 'w', delta: 15 }, { dimension: 'h', delta: 15 }],
|
|
310
312
|
sizeFlag: true,
|
|
@@ -482,6 +484,74 @@ describe('buildReviewScaffold — run-only verdict obligation', () => {
|
|
|
482
484
|
});
|
|
483
485
|
});
|
|
484
486
|
});
|
|
487
|
+
// ---- Signal C: element-presence facts (appearance container) ----
|
|
488
|
+
describe('derivePresenceFacts — set difference over element ids', () => {
|
|
489
|
+
test('equal element sets produce no facts', () => {
|
|
490
|
+
expect(derivePresenceFacts(['a', 'b', 'c'], ['a', 'b', 'c'])).toEqual([]);
|
|
491
|
+
});
|
|
492
|
+
test('an id only in the reference yields element_missing', () => {
|
|
493
|
+
// rendered lacks `voucher`, which the reference declares.
|
|
494
|
+
expect(derivePresenceFacts(['banner'], ['banner', 'voucher'])).toEqual([
|
|
495
|
+
{ kind: 'element_missing', elementId: 'voucher' },
|
|
496
|
+
]);
|
|
497
|
+
});
|
|
498
|
+
test('an id only in the render yields element_extra', () => {
|
|
499
|
+
// rendered has an extra `debugOverlay` the reference does not declare.
|
|
500
|
+
expect(derivePresenceFacts(['banner', 'debugOverlay'], ['banner'])).toEqual([
|
|
501
|
+
{ kind: 'element_extra', elementId: 'debugOverlay' },
|
|
502
|
+
]);
|
|
503
|
+
});
|
|
504
|
+
test('reports both a missing and an extra id, missing before extra', () => {
|
|
505
|
+
expect(derivePresenceFacts(['banner', 'extra'], ['banner', 'gone'])).toEqual([
|
|
506
|
+
{ kind: 'element_missing', elementId: 'gone' },
|
|
507
|
+
{ kind: 'element_extra', elementId: 'extra' },
|
|
508
|
+
]);
|
|
509
|
+
});
|
|
510
|
+
test('result is order-independent — same sets in any input order give the same facts', () => {
|
|
511
|
+
const a = derivePresenceFacts(['a', 'b', 'c'], ['b', 'd', 'a']);
|
|
512
|
+
const b = derivePresenceFacts(['c', 'b', 'a'], ['d', 'a', 'b']);
|
|
513
|
+
expect(a).toEqual(b);
|
|
514
|
+
// d missing, c extra — independent of how the ids were ordered or duplicated.
|
|
515
|
+
expect(derivePresenceFacts(['c', 'c', 'b', 'a'], ['a', 'b', 'd', 'd'])).toEqual([
|
|
516
|
+
{ kind: 'element_missing', elementId: 'd' },
|
|
517
|
+
{ kind: 'element_extra', elementId: 'c' },
|
|
518
|
+
]);
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
describe('buildStructuralBlock — appearance container (Signal C, additive)', () => {
|
|
522
|
+
test('no appearanceReference → no appearance container; facts + geometry unchanged', () => {
|
|
523
|
+
const withC = buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2);
|
|
524
|
+
const without = buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2, undefined, null);
|
|
525
|
+
expect(withC.appearance).toBeUndefined();
|
|
526
|
+
// Signal A facts and Signal B geometry are byte-identical whether or not the
|
|
527
|
+
// appearance arg is threaded.
|
|
528
|
+
expect(without).toEqual(withC);
|
|
529
|
+
});
|
|
530
|
+
test('with a figma appearanceReference → presence facts from rendered vs reference ids', () => {
|
|
531
|
+
// dump node ids: node-1, banner, voucher, copy; reference declares banner+voucher.
|
|
532
|
+
const block = buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2, undefined, {
|
|
533
|
+
baseline: 'figma',
|
|
534
|
+
elementIds: ['banner', 'voucher'],
|
|
535
|
+
});
|
|
536
|
+
expect(block.appearance).toEqual({
|
|
537
|
+
baseline: 'figma',
|
|
538
|
+
facts: [
|
|
539
|
+
{ kind: 'element_extra', elementId: 'copy' },
|
|
540
|
+
{ kind: 'element_extra', elementId: 'node-1' },
|
|
541
|
+
],
|
|
542
|
+
});
|
|
543
|
+
// Additive: the existing Signal A facts and Signal B geometry are untouched.
|
|
544
|
+
expect(block.facts).toEqual(buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2).facts);
|
|
545
|
+
expect(block.geometry).toEqual(buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2).geometry);
|
|
546
|
+
});
|
|
547
|
+
test('an appearanceReference matching the render → ran-and-clean (baseline, zero facts)', () => {
|
|
548
|
+
const block = buildStructuralBlock(HUB_PROMO_DUMP, null, 2, undefined, {
|
|
549
|
+
baseline: 'figma',
|
|
550
|
+
elementIds: ['node-1', 'banner', 'voucher', 'copy'],
|
|
551
|
+
});
|
|
552
|
+
expect(block.appearance).toEqual({ baseline: 'figma', facts: [] });
|
|
553
|
+
});
|
|
554
|
+
});
|
|
485
555
|
describe('embedded geometry stage parity', () => {
|
|
486
556
|
// The render ships a JS-source port of the differ + assembler. It must produce
|
|
487
557
|
// byte-identical blocks to the canonical typed module, or run-side structural
|
|
@@ -492,6 +562,23 @@ describe('embedded geometry stage parity', () => {
|
|
|
492
562
|
const reference = { card: { width: 328, height: 72 } };
|
|
493
563
|
expect(embedded.diffKeyGeometry(generated, reference)).toEqual(diffKeyGeometry(generated, reference));
|
|
494
564
|
});
|
|
565
|
+
test('embedded derivePresenceFacts matches the module (Signal C)', () => {
|
|
566
|
+
const cases = [
|
|
567
|
+
[['a', 'b', 'c'], ['a', 'b', 'c']],
|
|
568
|
+
[['banner'], ['banner', 'voucher']],
|
|
569
|
+
[['banner', 'debugOverlay'], ['banner']],
|
|
570
|
+
[['c', 'b', 'a'], ['d', 'a', 'b']],
|
|
571
|
+
[[], ['x', 'y']],
|
|
572
|
+
[['x', 'y'], []],
|
|
573
|
+
];
|
|
574
|
+
for (const [rendered, reference] of cases) {
|
|
575
|
+
expect(embedded.derivePresenceFacts(rendered, reference)).toEqual(derivePresenceFacts(rendered, reference));
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
test('embedded buildStructuralBlock matches the module with an appearanceReference', () => {
|
|
579
|
+
const appearanceReference = { baseline: 'figma', elementIds: ['banner', 'voucher'] };
|
|
580
|
+
expect(embedded.buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2, undefined, appearanceReference)).toEqual(buildStructuralBlock(HUB_PROMO_DUMP, HUB_PROMO_REFERENCE, 2, undefined, appearanceReference));
|
|
581
|
+
});
|
|
495
582
|
const parityCases = [
|
|
496
583
|
{ name: 'factsOnly', dump: HUB_PROMO_DUMP, ref: null },
|
|
497
584
|
{ name: 'withGeometry', dump: HUB_PROMO_DUMP, ref: HUB_PROMO_REFERENCE },
|
|
@@ -357,7 +357,8 @@ describe('android render runtime — render + trim (happy path)', () => {
|
|
|
357
357
|
const card = geometry?.elements.find((e) => e.id === 'card');
|
|
358
358
|
expect(card).toEqual({
|
|
359
359
|
id: 'card',
|
|
360
|
-
rendered
|
|
360
|
+
// rendered now carries x/y (dp); design stays w/h-only (no x/y in ref).
|
|
361
|
+
rendered: { w: 80, h: 90, x: 10, y: 10 },
|
|
361
362
|
design: { w: 80, h: 80 },
|
|
362
363
|
deltas: [{ dimension: 'h', delta: 10 }],
|
|
363
364
|
sizeFlag: true,
|