@invarn/cibuild 2.1.2 → 2.1.3
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.
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Structural-facts deriver — Signal A of the UI-fidelity structural pass.
|
|
3
3
|
*
|
|
4
|
-
* Pure and platform-agnostic: it takes a per-node layout dump (
|
|
5
|
-
*
|
|
6
|
-
* geometric facts the fidelity judge should check instead of
|
|
7
|
-
* by eye:
|
|
4
|
+
* Pure and platform-agnostic: it takes a per-node layout dump (clipped +
|
|
5
|
+
* unclipped bounds in px, parent) emitted alongside the render and derives
|
|
6
|
+
* intent-agnostic geometric facts the fidelity judge should check instead of
|
|
7
|
+
* discovering them by eye:
|
|
8
8
|
*
|
|
9
9
|
* - `overflow` — a node extends past its parent's box (parent does
|
|
10
10
|
* NOT clip, so the excess is visibly sticking out).
|
|
@@ -26,11 +26,18 @@ export interface LayoutBounds {
|
|
|
26
26
|
export interface LayoutNode {
|
|
27
27
|
/** Stable id/label for the node within one dump. */
|
|
28
28
|
id: string;
|
|
29
|
-
/**
|
|
29
|
+
/** Clipped root-space bounds in px (boundsInRoot — the visible region,
|
|
30
|
+
* clamped by any clipping ancestor). */
|
|
30
31
|
bounds: LayoutBounds;
|
|
32
|
+
/** Unclipped root-space bounds in px (positionInRoot + size — the true layout
|
|
33
|
+
* extent). Absent in legacy dumps emitted before the structural pass tracked
|
|
34
|
+
* it; when present, overflow/clip is derived from this so it survives a
|
|
35
|
+
* clamping parent (which would otherwise hide the excess in `bounds`). */
|
|
36
|
+
unclippedBounds?: LayoutBounds;
|
|
31
37
|
/** Parent node id, or null for a root node. */
|
|
32
38
|
parentId: string | null;
|
|
33
|
-
/**
|
|
39
|
+
/** Legacy clip hint (parent clips its children). The current emitter writes a
|
|
40
|
+
* constant false; used only for the legacy (no-`unclippedBounds`) fallback. */
|
|
34
41
|
clipsChildren: boolean;
|
|
35
42
|
}
|
|
36
43
|
export interface LayoutDump {
|
|
@@ -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
|
|
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;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvD,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;CAC3C,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1C,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;CAC5C,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,WAAW,4BAA4B;IAC3C;yEACqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;mCAC+B;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAoCD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,4BAAiC,GACzC,cAAc,EAAE,CAuHlB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAgJvC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gCAAgC,IAAI;IAClD,qBAAqB,EAAE,CACrB,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE,4BAA4B,KACnC,cAAc,EAAE,CAAC;CACvB,CAWA"}
|
|
Binary file
|
|
@@ -63,6 +63,75 @@ describe('deriveStructuralFacts — overflow vs clip', () => {
|
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
|
+
describe('deriveStructuralFacts — unclippedBounds path (real clamping renders)', () => {
|
|
67
|
+
// A real clipping parent CLAMPS the child's boundsInRoot to its own edge, so
|
|
68
|
+
// the clipped `bounds` show no excess — the overflow lives only in
|
|
69
|
+
// `unclippedBounds`. This is the case bounds-only v1 went blind on.
|
|
70
|
+
test('a clamped child is reported clipped-by-parent, recovered from unclippedBounds', () => {
|
|
71
|
+
const dump = {
|
|
72
|
+
canvas: { left: 0, top: 0, right: 360, bottom: 720 },
|
|
73
|
+
nodes: [
|
|
74
|
+
{
|
|
75
|
+
id: 'HubPromoBanner',
|
|
76
|
+
bounds: { left: 16, top: 100, right: 344, bottom: 262 },
|
|
77
|
+
unclippedBounds: { left: 16, top: 100, right: 344, bottom: 262 },
|
|
78
|
+
parentId: null,
|
|
79
|
+
clipsChildren: false,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
// visible bounds CLAMPED to the card bottom (262); the true extent runs
|
|
83
|
+
// 18px past it → 18px cut away. The current emitter writes clipsChildren
|
|
84
|
+
// false, so only unclippedBounds can reveal this.
|
|
85
|
+
id: 'voucherTag',
|
|
86
|
+
bounds: { left: 250, top: 210, right: 330, bottom: 262 },
|
|
87
|
+
unclippedBounds: { left: 250, top: 210, right: 330, bottom: 280 },
|
|
88
|
+
parentId: 'HubPromoBanner',
|
|
89
|
+
clipsChildren: false,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
const facts = deriveStructuralFacts(dump);
|
|
94
|
+
expect(facts).toHaveLength(1);
|
|
95
|
+
expect(facts[0]).toMatchObject({
|
|
96
|
+
kind: 'clipped_by_parent',
|
|
97
|
+
nodeId: 'voucherTag',
|
|
98
|
+
clipperId: 'HubPromoBanner',
|
|
99
|
+
edges: ['bottom'],
|
|
100
|
+
clippedPx: { bottom: 18 },
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
test('a child sticking out of a non-clipping parent (bounds == unclippedBounds) is overflow', () => {
|
|
104
|
+
const dump = {
|
|
105
|
+
canvas: CANVAS,
|
|
106
|
+
nodes: [
|
|
107
|
+
{
|
|
108
|
+
id: 'row',
|
|
109
|
+
bounds: { left: 20, top: 20, right: 180, bottom: 100 },
|
|
110
|
+
unclippedBounds: { left: 20, top: 20, right: 180, bottom: 100 },
|
|
111
|
+
parentId: null,
|
|
112
|
+
clipsChildren: false,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
// not clamped (bounds == unclipped) → the 15px excess is visible.
|
|
116
|
+
id: 'chip',
|
|
117
|
+
bounds: { left: 150, top: 30, right: 195, bottom: 80 },
|
|
118
|
+
unclippedBounds: { left: 150, top: 30, right: 195, bottom: 80 },
|
|
119
|
+
parentId: 'row',
|
|
120
|
+
clipsChildren: false,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
const facts = deriveStructuralFacts(dump);
|
|
125
|
+
expect(facts).toHaveLength(1);
|
|
126
|
+
expect(facts[0]).toMatchObject({
|
|
127
|
+
kind: 'overflow',
|
|
128
|
+
nodeId: 'chip',
|
|
129
|
+
parentId: 'row',
|
|
130
|
+
edges: ['right'],
|
|
131
|
+
overflowPx: { right: 15 },
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
66
135
|
describe('deriveStructuralFacts — off-screen', () => {
|
|
67
136
|
test('a node extending past the canvas is reported off-screen on those edges', () => {
|
|
68
137
|
const dump = {
|
|
@@ -138,6 +207,22 @@ describe('embedded render-side stage parity', () => {
|
|
|
138
207
|
{ id: 'b', bounds: { left: 80, top: 60, right: 180, bottom: 160 }, parentId: 'root', clipsChildren: false },
|
|
139
208
|
],
|
|
140
209
|
},
|
|
210
|
+
// New unclippedBounds path: a clamped child (clip) and a visible-excess child
|
|
211
|
+
// (overflow) — exercises the embedded port's new branch, not just the legacy one.
|
|
212
|
+
clipReal: {
|
|
213
|
+
canvas: CANVAS,
|
|
214
|
+
nodes: [
|
|
215
|
+
{ id: 'card', bounds: { left: 20, top: 20, right: 180, bottom: 200 }, unclippedBounds: { left: 20, top: 20, right: 180, bottom: 200 }, parentId: null, clipsChildren: false },
|
|
216
|
+
{ id: 'tag', bounds: { left: 60, top: 150, right: 140, bottom: 200 }, unclippedBounds: { left: 60, top: 150, right: 140, bottom: 224 }, parentId: 'card', clipsChildren: false },
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
overflowReal: {
|
|
220
|
+
canvas: CANVAS,
|
|
221
|
+
nodes: [
|
|
222
|
+
{ id: 'row', bounds: { left: 20, top: 20, right: 180, bottom: 100 }, unclippedBounds: { left: 20, top: 20, right: 180, bottom: 100 }, parentId: null, clipsChildren: false },
|
|
223
|
+
{ id: 'chip', bounds: { left: 150, top: 30, right: 195, bottom: 80 }, unclippedBounds: { left: 150, top: 30, right: 195, bottom: 80 }, parentId: 'row', clipsChildren: false },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
141
226
|
};
|
|
142
227
|
for (const [name, dump] of Object.entries(fixtures)) {
|
|
143
228
|
test(`embedded port matches the module for the ${name} fixture`, () => {
|