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>
67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
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)
|
|
})
|
|
})
|