@invarn/cibuild 2.2.3 → 2.2.4
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.
|
@@ -741,6 +741,33 @@ describe('buildStructuralBlock — appearance container (Signal C, additive)', (
|
|
|
741
741
|
},
|
|
742
742
|
]);
|
|
743
743
|
});
|
|
744
|
+
test('with reference text color → a text-channel color fact (issue 02)', () => {
|
|
745
|
+
// COLOR_DUMP renders `card` text #000000; the reference declares #ffffff for
|
|
746
|
+
// the text channel and the matching fill — so exactly one fact, channel:'text'.
|
|
747
|
+
const block = buildStructuralBlock(COLOR_DUMP, null, 2, undefined, {
|
|
748
|
+
baseline: 'figma',
|
|
749
|
+
elementIds: ['card'],
|
|
750
|
+
color: { card: { fill: '#ff0000', text: '#ffffff' } },
|
|
751
|
+
});
|
|
752
|
+
expect(block.appearance?.facts).toEqual([
|
|
753
|
+
{
|
|
754
|
+
kind: 'color',
|
|
755
|
+
elementId: 'card',
|
|
756
|
+
channel: 'text',
|
|
757
|
+
rendered: '#000000',
|
|
758
|
+
reference: '#ffffff',
|
|
759
|
+
deltaE: ciede2000(hexToLab('#000000'), hexToLab('#ffffff')),
|
|
760
|
+
},
|
|
761
|
+
]);
|
|
762
|
+
});
|
|
763
|
+
test('an identical text color → no text fact (issue 02)', () => {
|
|
764
|
+
const block = buildStructuralBlock(COLOR_DUMP, null, 2, undefined, {
|
|
765
|
+
baseline: 'figma',
|
|
766
|
+
elementIds: ['card'],
|
|
767
|
+
color: { card: { fill: '#ff0000', text: '#000000' } },
|
|
768
|
+
});
|
|
769
|
+
expect(block.appearance?.facts).toEqual([]);
|
|
770
|
+
});
|
|
744
771
|
test('an appearanceReference without color → no color facts (presence only)', () => {
|
|
745
772
|
const block = buildStructuralBlock(COLOR_DUMP, null, 2, undefined, {
|
|
746
773
|
baseline: 'figma',
|
|
@@ -822,6 +849,20 @@ describe('appearanceReferenceFromPreviousRender — previous render snapshot →
|
|
|
822
849
|
color: { banner: { fill: '#fff0ee' } },
|
|
823
850
|
});
|
|
824
851
|
});
|
|
852
|
+
test('a previous render with per-element text colors → the text channel rides through into the reference (issue 02)', () => {
|
|
853
|
+
// The mapper carries the whole previous color map, so the foreground (text)
|
|
854
|
+
// channel reaches the reference exactly as the fill channel does — channel
|
|
855
|
+
// genericity, not a fill-only carry.
|
|
856
|
+
const previous = {
|
|
857
|
+
elementIds: ['banner', 'copy'],
|
|
858
|
+
color: { banner: { fill: '#fff0ee' }, copy: { text: '#1b1b1b' } },
|
|
859
|
+
};
|
|
860
|
+
expect(appearanceReferenceFromPreviousRender(previous)).toEqual({
|
|
861
|
+
baseline: 'previous_render',
|
|
862
|
+
elementIds: ['banner', 'copy'],
|
|
863
|
+
color: { banner: { fill: '#fff0ee' }, copy: { text: '#1b1b1b' } },
|
|
864
|
+
});
|
|
865
|
+
});
|
|
825
866
|
test('a previous render with no extractable color → a presence-only reference (ids, no color)', () => {
|
|
826
867
|
const previous = { elementIds: ['banner', 'voucher'] };
|
|
827
868
|
expect(appearanceReferenceFromPreviousRender(previous)).toEqual({
|
|
@@ -870,6 +911,60 @@ describe('appearanceReferenceFromPreviousRender — previous render snapshot →
|
|
|
870
911
|
]);
|
|
871
912
|
expect(colorFacts[0].kind === 'color' && colorFacts[0].deltaE).toBeGreaterThan(10);
|
|
872
913
|
});
|
|
914
|
+
test('fed into buildStructuralBlock, a drifted text color produces a previous_render text-channel fact (issue 02)', () => {
|
|
915
|
+
// The render paints `copy` the wrong foreground color (#1e88e5); the previous
|
|
916
|
+
// render painted #1b1b1b. The mapper carries the text channel through, and the
|
|
917
|
+
// assembled block carries one text color fact with a large positive ΔE —
|
|
918
|
+
// distinct from fill, proven through the public interface.
|
|
919
|
+
const driftedDump = {
|
|
920
|
+
canvas: { left: 0, top: 0, right: 686, bottom: 282 },
|
|
921
|
+
nodes: [
|
|
922
|
+
{ id: 'copy', bounds: { left: 0, top: 0, right: 343, bottom: 40 }, parentId: null, clipsChildren: false, textColor: '#1e88e5' },
|
|
923
|
+
],
|
|
924
|
+
};
|
|
925
|
+
const previous = { elementIds: ['copy'], color: { copy: { text: '#1b1b1b' } } };
|
|
926
|
+
const reference = appearanceReferenceFromPreviousRender(previous);
|
|
927
|
+
const block = buildStructuralBlock(driftedDump, null, 2, undefined, reference);
|
|
928
|
+
expect(block.appearance?.baseline).toBe('previous_render');
|
|
929
|
+
const colorFacts = (block.appearance?.facts ?? []).filter((f) => f.kind === 'color');
|
|
930
|
+
expect(colorFacts).toEqual([
|
|
931
|
+
{
|
|
932
|
+
kind: 'color',
|
|
933
|
+
elementId: 'copy',
|
|
934
|
+
channel: 'text',
|
|
935
|
+
rendered: '#1e88e5',
|
|
936
|
+
reference: '#1b1b1b',
|
|
937
|
+
deltaE: ciede2000(hexToLab('#1e88e5'), hexToLab('#1b1b1b')),
|
|
938
|
+
},
|
|
939
|
+
]);
|
|
940
|
+
expect(colorFacts[0].kind === 'color' && colorFacts[0].deltaE).toBeGreaterThan(10);
|
|
941
|
+
});
|
|
942
|
+
test('a correct fill with a wrong text color yields a text-channel fact only — the two never collapse (issue 02)', () => {
|
|
943
|
+
// One element carries both channels: fill matches the previous render, text
|
|
944
|
+
// drifts. Exactly one fact, channel:'text' — the correct fill produces none.
|
|
945
|
+
const dump = {
|
|
946
|
+
canvas: { left: 0, top: 0, right: 343, bottom: 72 },
|
|
947
|
+
nodes: [
|
|
948
|
+
{ id: 'banner', bounds: { left: 0, top: 0, right: 343, bottom: 72 }, parentId: null, clipsChildren: false, fillColor: '#fff0ee', textColor: '#1e88e5' },
|
|
949
|
+
],
|
|
950
|
+
};
|
|
951
|
+
const previous = {
|
|
952
|
+
elementIds: ['banner'],
|
|
953
|
+
color: { banner: { fill: '#fff0ee', text: '#1b1b1b' } },
|
|
954
|
+
};
|
|
955
|
+
const block = buildStructuralBlock(dump, null, 2, undefined, appearanceReferenceFromPreviousRender(previous));
|
|
956
|
+
const colorFacts = (block.appearance?.facts ?? []).filter((f) => f.kind === 'color');
|
|
957
|
+
expect(colorFacts).toEqual([
|
|
958
|
+
{
|
|
959
|
+
kind: 'color',
|
|
960
|
+
elementId: 'banner',
|
|
961
|
+
channel: 'text',
|
|
962
|
+
rendered: '#1e88e5',
|
|
963
|
+
reference: '#1b1b1b',
|
|
964
|
+
deltaE: ciede2000(hexToLab('#1e88e5'), hexToLab('#1b1b1b')),
|
|
965
|
+
},
|
|
966
|
+
]);
|
|
967
|
+
});
|
|
873
968
|
});
|
|
874
969
|
// ---- Signal C: build the appearance reference from run params (issue 05) ----
|
|
875
970
|
//
|
|
@@ -1059,6 +1154,22 @@ describe('embedded geometry stage parity', () => {
|
|
|
1059
1154
|
};
|
|
1060
1155
|
expect(embedded.buildStructuralBlock(dumpWithColor, null, 2, undefined, appearanceReference)).toEqual(buildStructuralBlock(dumpWithColor, null, 2, undefined, appearanceReference));
|
|
1061
1156
|
});
|
|
1157
|
+
test('embedded buildStructuralBlock matches the module for a text-only color drift (issue 02)', () => {
|
|
1158
|
+
// Correct fill + wrong text: the embedded port must emit the same single
|
|
1159
|
+
// text-channel fact (no collapse), matching the typed module exactly.
|
|
1160
|
+
const dumpWithText = {
|
|
1161
|
+
canvas: { left: 0, top: 0, right: 343, bottom: 72 },
|
|
1162
|
+
nodes: [
|
|
1163
|
+
{ id: 'banner', bounds: { left: 0, top: 0, right: 343, bottom: 72 }, parentId: null, clipsChildren: false, fillColor: '#fff0ee', textColor: '#1e88e5' },
|
|
1164
|
+
],
|
|
1165
|
+
};
|
|
1166
|
+
const appearanceReference = {
|
|
1167
|
+
baseline: 'previous_render',
|
|
1168
|
+
elementIds: ['banner'],
|
|
1169
|
+
color: { banner: { fill: '#fff0ee', text: '#1b1b1b' } },
|
|
1170
|
+
};
|
|
1171
|
+
expect(embedded.buildStructuralBlock(dumpWithText, null, 2, undefined, appearanceReference)).toEqual(buildStructuralBlock(dumpWithText, null, 2, undefined, appearanceReference));
|
|
1172
|
+
});
|
|
1062
1173
|
const parityCases = [
|
|
1063
1174
|
{ name: 'factsOnly', dump: HUB_PROMO_DUMP, ref: null },
|
|
1064
1175
|
{ name: 'withGeometry', dump: HUB_PROMO_DUMP, ref: HUB_PROMO_REFERENCE },
|