editor: experiments QA panel (P0 of experiments plan)
New Experiments section in the rail: lists tracking experiment rules (status, traffic, arms, targeting), shows this session's assignment via evaluate-experiments, per-arm analytics with winner/significance, a QA arm override (sets the storefront variant cookie), and a two-step pause/resume kill-switch. Admin reads degrade when the tracking gate refuses; eligibility always works. Mock tracking endpoints + harness tests. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
1 parent
40b33f89a4
commit
922562e1c2
6 files changed
+325
-1
No files matched your search
@@ -0,0 +1,66 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { act } from 'react'
|
||||
import { setup } from './harness'
|
||||
import type { Harness } from './harness'
|
||||
|
||||
let h: Harness
|
||||
afterEach(() => h?.cleanup())
|
||||
|
||||
const q = (sel: string) => h.shadow.querySelector<HTMLElement>(sel)
|
||||
const qa = (sel: string) => [...h.shadow.querySelectorAll<HTMLElement>(sel)]
|
||||
|
||||
describe('experiments QA panel', () => {
|
||||
it('lists experiments with status, eligibility and analytics', async () => {
|
||||
h = await setup()
|
||||
await h.settle()
|
||||
await h.press('Panel')
|
||||
await h.press('Experiments')
|
||||
await h.settle()
|
||||
const rows = qa('.cms-exp')
|
||||
expect(rows.map((r) => r.querySelector('strong')?.textContent)).toEqual(['exp_hero', 'exp_checkout'])
|
||||
expect(rows[0].textContent).toContain('live')
|
||||
expect(rows[1].textContent).toContain('paused')
|
||||
expect(rows[0].textContent).toContain('traffic 100%')
|
||||
expect(rows[0].textContent).toContain('arms a / b')
|
||||
expect(rows[0].textContent).toContain('page_path prefix /')
|
||||
// Public evaluate: hero eligible (arm a), disabled checkout not.
|
||||
expect(rows[0].textContent).toContain('you: a')
|
||||
expect(rows[1].textContent).toContain('not eligible')
|
||||
// Admin analytics for the live rule.
|
||||
expect(rows[0].textContent).toContain('250 sessions')
|
||||
expect(rows[0].textContent).toContain('b')
|
||||
expect(rows[0].textContent).toContain('significant')
|
||||
})
|
||||
|
||||
it('forcing a QA arm sets the storefront variant cookie', async () => {
|
||||
h = await setup()
|
||||
await h.settle()
|
||||
await h.press('Panel')
|
||||
await h.press('Experiments')
|
||||
await h.settle()
|
||||
const sel = q('select[aria-label=\"Force variant for exp_hero\"]') as HTMLSelectElement
|
||||
const set = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, 'value')!.set!
|
||||
await act(async () => {
|
||||
set.call(sel, 'b')
|
||||
sel.dispatchEvent(new Event('change', { bubbles: true }))
|
||||
})
|
||||
expect(document.cookie).toContain('exp_exp_hero=b')
|
||||
})
|
||||
|
||||
it('pausing asks to confirm, then flips the rule via PATCH', async () => {
|
||||
h = await setup()
|
||||
await h.settle()
|
||||
await h.press('Panel')
|
||||
await h.press('Experiments')
|
||||
await h.settle()
|
||||
await act(async () => void q('[aria-label=\"Pause exp_hero\"]')!.click())
|
||||
await h.settle()
|
||||
// First click only arms the confirm …
|
||||
expect(q('[aria-label=\"Pause exp_hero\"]')?.textContent).toContain('confirm')
|
||||
await act(async () => void q('[aria-label=\"Pause exp_hero\"]')!.click())
|
||||
await h.settle()
|
||||
const rows = qa('.cms-exp')
|
||||
expect(rows[0].textContent).toContain('paused')
|
||||
expect(h.cms.requests.some((r) => r === 'PATCH /api/tracking/config/experiments/exp_hero/disabled')).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user