Skip to content

Commit 689ee5d

Browse files
test: add sibling shadow root components re-render isolation test using assert.step
Agent-Logs-Url: https://github.com/emberjs/ember.js/sessions/3810a6db-ae5e-4396-9f63-baf5a7164f82 Co-authored-by: NullVoxPopuli <[email protected]>
1 parent afb21ff commit 689ee5d

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

  • packages/@glimmer-workspace/integration-tests/lib/suites

packages/@glimmer-workspace/integration-tests/lib/suites/shadow-dom.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,119 @@ export class ShadowDOMSuite extends RenderTest {
222222
);
223223
}
224224

225+
@test
226+
'two sibling shadow root components: only the updated component re-renders'() {
227+
// Use closures so each component can call assert.step without needing access to the test
228+
const assert = this.assert;
229+
230+
class ComponentA extends GlimmerishComponent {
231+
@tracked value = 'a-initial';
232+
233+
constructor(owner: Owner, args: Dict) {
234+
super(owner, args);
235+
aInstance.capture(this);
236+
}
237+
238+
get renderedValue() {
239+
assert.step('component-a rendered');
240+
return this.value;
241+
}
242+
}
243+
244+
class ComponentB extends GlimmerishComponent {
245+
@tracked value = 'b-initial';
246+
247+
constructor(owner: Owner, args: Dict) {
248+
super(owner, args);
249+
bInstance.capture(this);
250+
}
251+
252+
get renderedValue() {
253+
assert.step('component-b rendered');
254+
return this.value;
255+
}
256+
}
257+
258+
// Capture holders declared after the class definitions but before render,
259+
// so TypeScript can resolve the type parameters.
260+
const aInstance = this.capture<ComponentA>();
261+
const bInstance = this.capture<ComponentB>();
262+
263+
this.registerComponent(
264+
'Glimmer',
265+
'ComponentA',
266+
'<template shadowrootmode="open"><span class="a">{{this.renderedValue}}</span></template>',
267+
ComponentA as any
268+
);
269+
this.registerComponent(
270+
'Glimmer',
271+
'ComponentB',
272+
'<template shadowrootmode="open"><span class="b">{{this.renderedValue}}</span></template>',
273+
ComponentB as any
274+
);
275+
276+
this.render('<div class="host-a"><ComponentA /></div><div class="host-b"><ComponentB /></div>');
277+
278+
// Both rendered on initial render — consume those steps
279+
this.assert.verifySteps(
280+
['component-a rendered', 'component-b rendered'],
281+
'both components render initially'
282+
);
283+
284+
const rootEl = castToBrowser(this.element, 'HTML');
285+
const hostA = rootEl.querySelector('.host-a') as HTMLElement | null;
286+
const hostB = rootEl.querySelector('.host-b') as HTMLElement | null;
287+
288+
this.assert.strictEqual(
289+
hostA?.shadowRoot?.querySelector('.a')?.textContent,
290+
'a-initial',
291+
'ComponentA initial value'
292+
);
293+
this.assert.strictEqual(
294+
hostB?.shadowRoot?.querySelector('.b')?.textContent,
295+
'b-initial',
296+
'ComponentB initial value'
297+
);
298+
299+
// Mutate only ComponentA — ComponentB must NOT re-render
300+
aInstance.captured.value = 'a-updated';
301+
this.rerender();
302+
303+
this.assert.strictEqual(
304+
hostA?.shadowRoot?.querySelector('.a')?.textContent,
305+
'a-updated',
306+
'ComponentA updated after tracked mutation'
307+
);
308+
this.assert.strictEqual(
309+
hostB?.shadowRoot?.querySelector('.b')?.textContent,
310+
'b-initial',
311+
'ComponentB unchanged'
312+
);
313+
this.assert.verifySteps(
314+
['component-a rendered'],
315+
'only ComponentA re-renders when its tracked value changes'
316+
);
317+
318+
// Mutate only ComponentB — ComponentA must NOT re-render
319+
bInstance.captured.value = 'b-updated';
320+
this.rerender();
321+
322+
this.assert.strictEqual(
323+
hostA?.shadowRoot?.querySelector('.a')?.textContent,
324+
'a-updated',
325+
'ComponentA still unchanged'
326+
);
327+
this.assert.strictEqual(
328+
hostB?.shadowRoot?.querySelector('.b')?.textContent,
329+
'b-updated',
330+
'ComponentB updated after tracked mutation'
331+
);
332+
this.assert.verifySteps(
333+
['component-b rendered'],
334+
'only ComponentB re-renders when its tracked value changes'
335+
);
336+
}
337+
225338
@test
226339
'conditional <template shadowrootmode="open"> inside a host element attaches shadow root when rendered'() {
227340
this.render(

0 commit comments

Comments
 (0)