Files
pbakaus_impeccable/tests/live-e2e-agent-output.test.mjs
T
Abdul WahabandGitHub 84135db0e6 Add DeepSeek live E2E adapter (#163)
* Add DeepSeek live E2E adapter

* Fix DeepSeek live E2E review issues

* Harden live-e2e helpers against silent failures

- htmlToJsx: match multi-line inline style attributes ([\s\S]*?)
- readCliOption: throw when --flag value is missing or another --flag
- llm-agent: echo parsed payload (first 500 chars) in schema-error throws

* Bind hoisted inline styles to their owning tag

normalizeVariantOutput previously hoisted every stripped style attribute
onto a selector derived from the variant's first tag, so a style on a
nested <span> landed on <h1>. Now walks each opening tag and emits one
rule per styled element with a descendant combinator so nested-element
styles target the correct node. Also fixes the duplicated multi-line
style regex bug (.*?) -> ([\s\S]*?) that survived the previous round.

Extracts parseVariantResponse from llm-agent for direct schema-throw
testing, and lifts readCliOption into its own module so its new
missing-value throws can be unit-tested.

Adds tests for:
- multi-line style hoisting
- nested-element tag binding and per-tag rule emission
- astro-global-prefixed selector shape
- no-op identity-return path
- opts.config short-circuit in createLlmAgent
- all four parseVariantResponse schema previews + JSON-parse failure
- readCliOption value/throw matrix

* Hoist inline styles via data attribute, not tag name

Two bugs in normalizeVariantOutput that Bugbot flagged:

1. Hoisted rules like `:scope span` matched every same-tag descendant of
   the variant wrap, so a style on one of several <span>s leaked onto its
   siblings.
2. The opening-tag scan used `[^>]*` for attributes, so a literal `>`
   inside a quoted attribute value (e.g. `aria-label="x > y"`) terminated
   the match early and the trailing `style="..."` was never seen.

stripInlineStylesPerElement now walks each opening tag character by
character respecting quoted attribute values, and tags every styled
element with `data-impeccable-hoist-id="N"`. Rules select on the
attribute so they bind to exactly the one element they came from.
The attribute is stripped during carbonize cleanup so it does not
survive into the final source.

* Harden live E2E variant CSS normalization

* Fix Radix tests

* Harden live E2E pick clicks
2026-05-22 09:28:36 -07:00

227 lines
7.7 KiB
JavaScript

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { htmlToJsx, normalizeVariantOutput } from './live-e2e/agent.mjs';
describe('live-e2e agent output translation', () => {
it('converts HTML class and inline style attributes to JSX syntax', () => {
const jsx = htmlToJsx(
'<h1 class="hero-title" style="--p-scale:1; font-size:2.25rem; font-weight:700">Title</h1>',
);
assert.equal(
jsx,
'<h1 className="hero-title" style={{ "--p-scale": "1", fontSize: "2.25rem", fontWeight: "700" }}>Title</h1>',
);
});
it('camel-cases vendor-prefixed style properties', () => {
const jsx = htmlToJsx(
'<h1 class="hero-title" style="-webkit-background-clip:text; background-clip:text; color:transparent">Title</h1>',
);
assert.equal(
jsx,
'<h1 className="hero-title" style={{ WebkitBackgroundClip: "text", backgroundClip: "text", color: "transparent" }}>Title</h1>',
);
});
it('keeps semicolons inside quoted and parenthesized style values', () => {
const jsx = htmlToJsx(
`<h1 class="hero-title" style='content:"a;b"; background-image:url("foo;bar"); font-size:1rem'>Title</h1>`,
);
assert.equal(
jsx,
`<h1 className="hero-title" style={{ content: "\\"a;b\\"", backgroundImage: "url(\\"foo;bar\\")", fontSize: "1rem" }}>Title</h1>`,
);
});
it('does not rewrite class inside data-class attributes', () => {
const jsx = htmlToJsx('<h1 data-class="hero" class="hero-title">Title</h1>');
assert.equal(jsx, '<h1 data-class="hero" className="hero-title">Title</h1>');
});
it('hoists model inline styles into variant-scoped CSS', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{
innerHtml: '<h1 class="hero-title" style="color:red; font-weight:700">Title</h1>',
},
{
innerHtml: `<h1 class="hero-title" style='content:"a;b"; background-image:url("foo;bar")'>Title</h1>`,
},
],
},
{ styleMode: 'scoped' },
);
assert.equal(
output.variants[0].innerHtml,
'<h1 data-impeccable-hoist-id="1" class="hero-title">Title</h1>',
);
assert.equal(
output.variants[1].innerHtml,
'<h1 data-impeccable-hoist-id="1" class="hero-title">Title</h1>',
);
assert.match(output.scopedCss, /@scope \(\[data-impeccable-variant="1"\]\)/);
assert.match(output.scopedCss, /:scope \[data-impeccable-hoist-id="1"\]\s*\{/);
assert.match(output.scopedCss, /color: red;/);
assert.match(output.scopedCss, /font-weight: 700;/);
assert.match(output.scopedCss, /content: "a;b";/);
assert.match(output.scopedCss, /background-image: url\("foo;bar"\);/);
});
it('hoists styles split across multiple lines', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{
innerHtml: '<h1 class="hero-title" style="\n color: red;\n font-size: 2rem;\n">Title</h1>',
},
],
},
{ styleMode: 'scoped' },
);
assert.equal(
output.variants[0].innerHtml,
'<h1 data-impeccable-hoist-id="1" class="hero-title">Title</h1>',
);
assert.match(output.scopedCss, /color: red;/);
assert.match(output.scopedCss, /font-size: 2rem;/);
});
it('binds hoisted rules to the element the style was on, not the variant root', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{
innerHtml: '<h1 class="hero-title"><span style="color:red; transform:scale(1.1)">Title</span></h1>',
},
],
},
{ styleMode: 'scoped' },
);
assert.equal(
output.variants[0].innerHtml,
'<h1 class="hero-title"><span data-impeccable-hoist-id="1">Title</span></h1>',
);
assert.match(output.scopedCss, /:scope \[data-impeccable-hoist-id="1"\]\s*\{/);
assert.match(output.scopedCss, /color: red;/);
assert.match(output.scopedCss, /transform: scale\(1\.1\);/);
});
it('emits a separate rule per styled element inside one variant', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{
innerHtml: '<h1 style="color:red"><span style="font-weight:700">Title</span></h1>',
},
],
},
{ styleMode: 'scoped' },
);
assert.match(output.scopedCss, /:scope \[data-impeccable-hoist-id="1"\]\s*\{[^}]*color: red;/);
assert.match(output.scopedCss, /:scope \[data-impeccable-hoist-id="2"\]\s*\{[^}]*font-weight: 700;/);
});
it('targets only the styled element when same-tag siblings are present', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{
innerHtml: '<div><span>plain</span><span style="color:red">styled</span></div>',
},
],
},
{ styleMode: 'scoped' },
);
const hoistMatches = output.variants[0].innerHtml.match(/data-impeccable-hoist-id=/g) || [];
assert.equal(hoistMatches.length, 1, 'only the styled span should carry the hoist attribute');
assert.match(
output.variants[0].innerHtml,
/<span data-impeccable-hoist-id="1">styled<\/span>/,
);
assert.match(output.variants[0].innerHtml, /<span>plain<\/span>/);
assert.match(output.scopedCss, /:scope \[data-impeccable-hoist-id="1"\]\s*\{[^}]*color: red;/);
});
it('handles > inside a quoted attribute value without losing the style', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{
innerHtml: '<h1 aria-label="x > y" style="color:red">Title</h1>',
},
],
},
{ styleMode: 'scoped' },
);
assert.match(output.variants[0].innerHtml, /aria-label="x > y"/);
assert.match(output.variants[0].innerHtml, /data-impeccable-hoist-id="1"/);
assert.doesNotMatch(output.variants[0].innerHtml, /style=/);
assert.match(output.scopedCss, /color: red;/);
});
it('emits the astro-global-prefixed selector shape when styleMode requests it', () => {
const output = normalizeVariantOutput(
{
scopedCss: '',
variants: [
{ innerHtml: '<h1 style="color:red">Title</h1>' },
],
},
{ styleMode: 'astro-global-prefixed' },
);
assert.match(
output.scopedCss,
/\[data-impeccable-variant="1"\] \[data-impeccable-hoist-id="1"\] \{/,
);
assert.doesNotMatch(output.scopedCss, /@scope/);
});
it('fills missing base variant rules when a model emits only param-conditioned CSS', () => {
const output = normalizeVariantOutput(
{
scopedCss: [
'@scope ([data-impeccable-variant="1"]) { :scope > h1 { color: blue; } }',
'@scope ([data-impeccable-variant="2"][data-p-uppercase]) { :scope > h1 { text-transform: uppercase; } }',
].join('\n'),
variants: [
{ innerHtml: '<h1 class="hero-title">One</h1>' },
{ innerHtml: '<h1 class="hero-title">Two</h1>' },
],
},
{ styleMode: 'scoped' },
);
assert.match(output.scopedCss, /@scope \(\[data-impeccable-variant="2"\]\)/);
assert.match(output.scopedCss, /--impeccable-variant-ready: 1;/);
assert.match(output.scopedCss, /@scope \(\[data-impeccable-variant="2"\]\[data-p-uppercase\]\)/);
});
it('returns the original output untouched when no inline styles are present', () => {
const original = {
scopedCss: '@scope ([data-impeccable-variant="1"]) { :scope > h1 { color: blue; } }',
variants: [{ innerHtml: '<h1 class="hero-title">Title</h1>' }],
};
const result = normalizeVariantOutput(original, { styleMode: 'scoped' });
assert.equal(result, original);
});
});