Skip to content

Commit 1bbc278

Browse files
liuluochen6-afhi-ogawaOpenCode (gpt-5.6-sol)
authored
fix(browser): preserve trace popover state (fix #10906) (#11021)
Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode (gpt-5.6-sol) <noreply@opencode.ai> Co-authored-by: Hiroshi Ogawa <hi.ogawa.zz@gmail.com>
1 parent c6174a6 commit 1bbc278

4 files changed

Lines changed: 45 additions & 7 deletions

File tree

packages/browser/src/client/tester/trace.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,23 @@ interface TraceSnapshot {
5555
pseudoClassIds: Record<PseudoClassName, number[]>
5656
}
5757

58-
// rrweb-snapshot rewrites pseudo-class selectors in serialized styles so replay can
59-
// reproduce snapshot-time states. For example:
58+
// Dynamic pseudo-class state isn't preserved in the serialized DOM. rrweb-snapshot
59+
// rewrites user-action selectors in serialized styles. For example:
6060
// some-selector:hover { ... }
6161
// becomes:
6262
// some-selector:hover, some-selector.\:hover { ... }
6363
// Vitest side integration then adds matching pseudo-state classes in the replay DOM.
6464
// rrweb-snapshot only handles `:hover` upstream, so we patch it locally for the
65-
// other user-action pseudo-classes as well.
65+
// other user-action pseudo-classes as well. Native states such as `:popover-open`
66+
// are restored through their DOM API instead.
6667
// https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Pseudo-classes#user_action_pseudo-classes
6768
const PSEUDO_CLASS_NAMES = [
6869
':hover',
6970
':active',
7071
':focus',
7172
':focus-visible',
7273
':focus-within',
74+
':popover-open',
7375
] as const
7476
type PseudoClassName = (typeof PSEUDO_CLASS_NAMES)[number]
7577

packages/ui/client/components/trace/TraceView.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ watch([selectedStep, iframeEl], ([step, iframe]) => {
8383
})
8484
for (const [className, ids] of Object.entries(pseudoClassIds)) {
8585
for (const id of ids) {
86-
const el = mirror.getNode(id) as Element | null
87-
if (el?.classList) {
86+
const el = mirror.getNode(id) as HTMLElement | null
87+
if (className === ':popover-open') {
88+
el?.showPopover?.()
89+
}
90+
else if (el?.classList) {
8891
el.classList.add(className)
8992
}
9093
}

test/ui/fixtures/trace/basic.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ test('switch-target', async () => {
1818
await page.getByRole('button').mark('Render switch target')
1919
})
2020

21+
test('popover', async () => {
22+
document.body.innerHTML = '<div popover="auto" style="inset: auto; top: 0; left: 0; margin: 0">Popover content</div>'
23+
const popover = document.querySelector<HTMLElement>('[popover]')!
24+
const popoverContent = page.getByText('Popover content')
25+
await popoverContent.mark('Render closed popover')
26+
popover.showPopover()
27+
await popoverContent.mark('Render open popover')
28+
})
29+
2130
test('pseudo-state', async () => {
2231
document.body.innerHTML = `
2332
<style>

test/ui/test/trace.spec.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ test.describe('ui', () => {
2626

2727
test.beforeEach(async ({ page }) => {
2828
await page.goto(baseURL)
29-
await assertTestCounts(page, { pass: 12, fail: 0 })
29+
await assertTestCounts(page, { pass: 13, fail: 0 })
3030
})
3131

3232
test('basic', async ({ page }) => {
@@ -37,6 +37,10 @@ test.describe('ui', () => {
3737
await testViewport(page)
3838
})
3939

40+
test('popover', async ({ page }) => {
41+
await testPopover(page)
42+
})
43+
4044
test('pseudo-state', async ({ page }) => {
4145
await testPseudoState(page)
4246
})
@@ -96,7 +100,7 @@ test.describe('html reporter', () => {
96100

97101
test.beforeEach(async ({ page }) => {
98102
await page.goto(baseURL)
99-
await assertTestCounts(page, { pass: 12, fail: 0 })
103+
await assertTestCounts(page, { pass: 13, fail: 0 })
100104
})
101105

102106
test('basic', async ({ page }) => {
@@ -107,6 +111,10 @@ test.describe('html reporter', () => {
107111
await testViewport(page)
108112
})
109113

114+
test('popover', async ({ page }) => {
115+
await testPopover(page)
116+
})
117+
110118
test('pseudo-state', async ({ page }) => {
111119
await testPseudoState(page)
112120
})
@@ -218,6 +226,22 @@ async function testViewport(page: Page) {
218226
await expect(traceFrame.locator('.viewport-pass')).toBeVisible()
219227
}
220228

229+
async function testPopover(page: Page) {
230+
await openExplorerItem(page, 'popover')
231+
232+
const traceView = page.getByTestId('trace-view')
233+
const traceSteps = traceView.getByTestId('trace-step-name')
234+
const traceFrame = traceView.frameLocator('iframe')
235+
const popoverContent = traceFrame.getByText('Popover content')
236+
await expect(traceView).toBeVisible()
237+
await traceSteps.getByText('Render closed popover').click()
238+
await expect(popoverContent).toBeHidden()
239+
await traceSteps.getByText('Render open popover').click()
240+
await expect(popoverContent).toBeVisible()
241+
await traceSteps.getByText('Render closed popover').click()
242+
await expect(popoverContent).toBeHidden()
243+
}
244+
221245
async function testPseudoState(page: Page) {
222246
await openExplorerItem(page, 'pseudo-state')
223247

0 commit comments

Comments
 (0)
Sponsor
SponsoredKunjungi sekarang
Promo