CONCEPT Cited by 1 source
DOM context injection for LLM¶
Definition¶
DOM context injection for LLM is the technique of capturing the actual rendered DOM of a UI component at test-run time and feeding it to an LLM as disambiguating context for a downstream task (typically code conversion or generation) that depends on what the DOM actually looks like — which the source code alone does not fully reveal.
It's a specialisation of the more general "give the LLM the runtime artifact, not just the source" technique: - For SQL-generation tasks, this would be sample rows or the actual table schema at query time. - For API-migration tasks, this would be a recorded request / response pair. - For front-end test migration, it's the rendered DOM per test case.
Why it matters for Enzyme → RTL migration¶
systems/react-testing-library queries the rendered DOM
(getByRole, getByText, getByTestId etc.), while
systems/enzyme queries the component tree
(wrapper.find('ComponentName')). Picking the right RTL query
for a given Enzyme call requires knowing:
- Does the component render as an element with a detectable
ARIA role? →
getByRole(...) - Does it have a
data-qa/data-testidattribute? →getByTestId(...) - Does it render visible text that's unique enough to query
against? →
getByText(...)
None of this is inferable from the test source file alone. The test file creates the component; the rendered DOM depends on the component's implementation, the props passed in, any feature flags, any conditional rendering based on props/state.
Slack's retrospective puts the problem cleanly:
"the choice between
getByRoleandgetByTestIddepended on the accessibility roles or test IDs present in the rendered component. However, AST lacks the capability to incorporate such contextual information. Its functionality is confined to processing the conversion logic based solely on the contents of the file being transformed, without consideration for external sources such as the actual DOM or React component code." (Source: sources/2024-06-19-slack-ai-powered-conversion-from-enzyme-to-react-testing-library)
If the AST can't see the DOM, neither can an LLM prompted only with the test source. The structural fix is to inject the DOM as context.
Mechanism (Slack's implementation)¶
Slack instruments Enzyme's own render methods to capture the rendered HTML per test case:
// Import original methods
import enzyme, { mount as originalMount, shallow as originalShallow } from 'enzyme';
import fs from 'fs';
let currentTestCaseName: string | null = null;
beforeEach(() => {
// Set the current test case name before each test
const testName = expect.getState().currentTestName;
currentTestCaseName = testName ? testName.trim() : null;
});
afterEach(() => {
// Reset the current test case name after each test
currentTestCaseName = null;
});
// Override mount method
enzyme.mount = (node: React.ReactElement, options?: enzyme.MountRendererProps) => {
const wrapper = originalMount(node, options);
const htmlContent = wrapper.html();
if (process.env.DOM_TREE_FILE) {
fs.appendFileSync(
process.env.DOM_TREE_FILE,
`<test_case_title>${currentTestCaseName}</test_case_title> and <dom_tree>${htmlContent}</dom_tree>;\n`,
);
}
return wrapper;
};
// ... same treatment for shallow
The output file contains one <test_case_title>...</test_case_title>
and <dom_tree>...</dom_tree> pair per test case. The pipeline
then injects these pairs into the LLM prompt inside
<component> tags, so each test case the LLM converts has the
exact DOM it was rendering against.
Why per-test-case, not per-component¶
A single test file typically contains many test cases
(it('...', () => { ... })), each potentially passing different
props or triggering different conditional rendering. Slack:
"This collection step was essential because each test case might have different setups and properties passed to the component, resulting in varying DOM structures for each test case."
Capturing DOM per-component would smear the signal across all test cases' DOMs; capturing per-test-case preserves the case-specific context the LLM needs.
Generalisation¶
The pattern — instrument the runtime, capture the artifact, inject it into the LLM prompt — generalises to:
- Schema migration: capture live row samples per source table, inject into prompt for SQL rewrite.
- API migration: capture recorded request/response pairs, inject into the prompt for client-code rewrite.
- i18n migration: capture rendered strings at runtime, inject as the set of strings that need translation keys.
- Config migration: capture the resolved config at runtime, inject alongside the raw config file.
The common shape: the source code is insufficient; the runtime state carries information the LLM needs.
Seen in¶
- sources/2024-06-19-slack-ai-powered-conversion-from-enzyme-to-react-testing-library
— Slack's Enzyme-to-RTL codemod captures per-test-case DOM
via Enzyme render-method instrumentation, injects it as
<component><test_case_title>...</test_case_title> and <dom_tree>...</dom_tree></component>blocks in the LLM prompt. A core component of why the hybrid AST + LLM pipeline reached ~80% conversion quality versus 40-60% for pure-LLM prompting.
Related¶
- concepts/llm-conversion-hallucination-control — the parent problem class
- concepts/abstract-syntax-tree — the complementary source-side pre-pass
- patterns/ast-plus-llm-hybrid-conversion — the composed pipeline
- systems/enzyme-to-rtl-codemod — production instantiation
- systems/react-testing-library — why DOM-based queries require DOM context