|
| 1 | +// E2E coverage for new UI components added by bb0 epic (v2): |
| 2 | +// DimensionsTab, AblationsTab, ScheduleEditor, BrickContextPanel, |
| 3 | +// Tooltip + ExplainModal, AutoGroupButton, Roundtrip badge. |
| 4 | + |
| 5 | +import { test, expect } from "@playwright/test"; |
| 6 | +import { |
| 7 | + gotoApp, selectPreset, clickTab, dropBrickViaPalette, |
| 8 | +} from "../fixtures"; |
| 9 | +import { snapshot } from "../utils/screenshot"; |
| 10 | +import { loadMatrix } from "../utils/matrix"; |
| 11 | + |
| 12 | +// --------------------------------------------------------------------------- |
| 13 | +// New sidebar tabs (Dimensions / Ablations) |
| 14 | +// --------------------------------------------------------------------------- |
| 15 | + |
| 16 | +test.describe("new sidebar tabs (E7-2 / E7-11-UI)", () => { |
| 17 | + test("Sidebar exposes Dimensions + Ablations tabs", async ({ page }) => { |
| 18 | + await gotoApp(page); |
| 19 | + await expect(page.getByTestId("sidebar-tab-dimensions")).toBeVisible(); |
| 20 | + await expect(page.getByTestId("sidebar-tab-ablations")).toBeVisible(); |
| 21 | + }); |
| 22 | + |
| 23 | + test("DimensionsTab populates after preset drop", async ({ page }) => { |
| 24 | + await gotoApp(page); |
| 25 | + await selectPreset(page, "llama3_8b"); |
| 26 | + await page.getByTestId("sidebar-tab-dimensions").click(); |
| 27 | + await page.getByTestId("dimensions-tab").waitFor(); |
| 28 | + // Wait for inference_log to populate (after verify completes). |
| 29 | + await expect.poll(async () => |
| 30 | + await page.locator("[data-testid^='dim-row-']").count(), |
| 31 | + { timeout: 8_000 }, |
| 32 | + ).toBeGreaterThan(0); |
| 33 | + await snapshot(page, "10_new_ui", "dimensions_populated"); |
| 34 | + }); |
| 35 | + |
| 36 | + test("DimensionsTab source filter narrows rows", async ({ page }) => { |
| 37 | + await gotoApp(page); |
| 38 | + await selectPreset(page, "llama3_8b"); |
| 39 | + await page.getByTestId("sidebar-tab-dimensions").click(); |
| 40 | + await page.getByTestId("dimensions-tab").waitFor(); |
| 41 | + await expect.poll(async () => |
| 42 | + await page.locator("[data-testid^='dim-row-']").count(), |
| 43 | + { timeout: 8_000 }, |
| 44 | + ).toBeGreaterThan(0); |
| 45 | + const before = await page.locator("[data-testid^='dim-row-']").count(); |
| 46 | + await page.getByTestId("dimensions-filter-source") |
| 47 | + .selectOption("user"); |
| 48 | + const after = await page.locator("[data-testid^='dim-row-']").count(); |
| 49 | + expect(after).toBeLessThanOrEqual(before); |
| 50 | + }); |
| 51 | + |
| 52 | + test("AblationsTab axis dropdown + variants render", async ({ page }) => { |
| 53 | + await gotoApp(page); |
| 54 | + await page.getByTestId("sidebar-tab-ablations").click(); |
| 55 | + await page.getByTestId("ablations-tab").waitFor(); |
| 56 | + await expect(page.getByTestId("ablation-axis")).toBeVisible(); |
| 57 | + await expect(page.getByTestId("ablation-run")).toBeVisible(); |
| 58 | + // Default activation axis exposes glu + swiglu variants pre-ticked. |
| 59 | + await expect(page.getByTestId("ablation-variant-glu")).toBeVisible(); |
| 60 | + await expect(page.getByTestId("ablation-variant-swiglu")).toBeVisible(); |
| 61 | + }); |
| 62 | + |
| 63 | + test("AblationsTab axis switch reveals new variant list", async ({ page }) => { |
| 64 | + await gotoApp(page); |
| 65 | + await page.getByTestId("sidebar-tab-ablations").click(); |
| 66 | + await page.getByTestId("ablations-tab").waitFor(); |
| 67 | + await page.getByTestId("ablation-axis").selectOption("optimizer"); |
| 68 | + await expect(page.getByTestId("ablation-variant-lion")).toBeVisible(); |
| 69 | + await expect(page.getByTestId("ablation-variant-muon")).toBeVisible(); |
| 70 | + }); |
| 71 | + |
| 72 | + test("AblationsTab Run with empty canvas surfaces error", async ({ page }) => { |
| 73 | + await gotoApp(page); |
| 74 | + await page.getByTestId("sidebar-tab-ablations").click(); |
| 75 | + await page.getByTestId("ablations-tab").waitFor(); |
| 76 | + await page.getByTestId("ablation-run").click(); |
| 77 | + await expect(page.getByTestId("ablation-error")).toBeVisible({ timeout: 4_000 }); |
| 78 | + }); |
| 79 | +}); |
| 80 | + |
| 81 | +// --------------------------------------------------------------------------- |
| 82 | +// ScheduleEditor inside OptimTab (E7-9) |
| 83 | +// --------------------------------------------------------------------------- |
| 84 | + |
| 85 | +test.describe("ScheduleEditor in OptimTab (E7-9)", () => { |
| 86 | + test("schedule toggle button reveals editor", async ({ page }) => { |
| 87 | + await gotoApp(page); |
| 88 | + await page.getByTestId("sidebar-tab-optim").click(); |
| 89 | + await page.getByTestId("optim-tab").waitFor(); |
| 90 | + await page.getByTestId("optim-group-0-schedule-toggle").click(); |
| 91 | + await expect(page.getByTestId("schedule-editor-0")).toBeVisible(); |
| 92 | + }); |
| 93 | + |
| 94 | + test("selecting cosine reveals total_steps + sparkline", async ({ page }) => { |
| 95 | + await gotoApp(page); |
| 96 | + await page.getByTestId("sidebar-tab-optim").click(); |
| 97 | + await page.getByTestId("optim-group-0-schedule-toggle").click(); |
| 98 | + await page.getByTestId("schedule-kind-0").selectOption("cosine"); |
| 99 | + await expect(page.getByTestId("schedule-total-0")).toBeVisible(); |
| 100 | + await expect(page.getByTestId("schedule-sparkline")).toBeVisible(); |
| 101 | + await snapshot(page, "10_new_ui", "schedule_cosine"); |
| 102 | + }); |
| 103 | +}); |
| 104 | + |
| 105 | +// --------------------------------------------------------------------------- |
| 106 | +// AutoGroupButton in OptimTab (E7-4) |
| 107 | +// --------------------------------------------------------------------------- |
| 108 | + |
| 109 | +test.describe("AutoGroupButton (E7-4)", () => { |
| 110 | + test("button visible in OptimTab; disabled until canvas has bricks", async ({ page }) => { |
| 111 | + await gotoApp(page); |
| 112 | + await page.getByTestId("sidebar-tab-optim").click(); |
| 113 | + const btn = page.getByTestId("optim-auto-group"); |
| 114 | + await expect(btn).toBeVisible(); |
| 115 | + await expect(btn).toBeDisabled(); |
| 116 | + }); |
| 117 | + |
| 118 | + test("Auto-group after preset emits banner with grouping", async ({ page }) => { |
| 119 | + await gotoApp(page); |
| 120 | + await selectPreset(page, "llama3_8b"); |
| 121 | + await page.getByTestId("sidebar-tab-optim").click(); |
| 122 | + await page.getByTestId("optim-auto-group").click(); |
| 123 | + await expect(page.getByTestId("optim-auto-group-banner")) |
| 124 | + .toBeVisible({ timeout: 8_000 }); |
| 125 | + }); |
| 126 | +}); |
| 127 | + |
| 128 | +// --------------------------------------------------------------------------- |
| 129 | +// BrickContextPanel (E7-5/E7-6) |
| 130 | +// --------------------------------------------------------------------------- |
| 131 | + |
| 132 | +test.describe("BrickContextPanel (E7-5 + E7-6)", () => { |
| 133 | + test("clicking a brick opens activation/norm panel", async ({ page }) => { |
| 134 | + await gotoApp(page); |
| 135 | + await dropBrickViaPalette(page, "mlp"); |
| 136 | + // pick first brick-node |
| 137 | + const firstNode = page.locator("[data-testid^='brick-node-']").first(); |
| 138 | + await firstNode.click(); |
| 139 | + // The panel testid uses the node id; locate any brick-context-* root |
| 140 | + const panel = page.locator("[data-testid^='brick-context-']").first(); |
| 141 | + await expect(panel).toBeVisible({ timeout: 4_000 }); |
| 142 | + // mlp should expose activation dropdown |
| 143 | + const actDropdown = page.locator( |
| 144 | + "[data-testid$='-activation']").first(); |
| 145 | + await expect(actDropdown).toBeVisible(); |
| 146 | + }); |
| 147 | +}); |
| 148 | + |
| 149 | +// --------------------------------------------------------------------------- |
| 150 | +// Roundtrip badge (E7-3-UI) |
| 151 | +// --------------------------------------------------------------------------- |
| 152 | + |
| 153 | +test("Roundtrip badge surfaces per-row OK/FAIL (E7-3-UI)", async ({ page }) => { |
| 154 | + const matrix = loadMatrix(); |
| 155 | + await gotoApp(page); |
| 156 | + await clickTab(page, "data"); |
| 157 | + await page.getByTestId("data-inspector").waitFor(); |
| 158 | + await page.getByTestId("data-path") |
| 159 | + .fill(matrix.parquets.T2_gpt2_small__P1_minimal.path); |
| 160 | + await page.getByTestId("data-load").click(); |
| 161 | + await page.getByTestId("data-row-0").waitFor({ timeout: 8_000 }); |
| 162 | + |
| 163 | + await page.getByTestId("data-tokenizer-path") |
| 164 | + .fill(matrix.tokenizers.T2_gpt2_small.path); |
| 165 | + await page.getByTestId("data-roundtrip").click(); |
| 166 | + await page.getByTestId("data-roundtrip-0").waitFor({ timeout: 8_000 }); |
| 167 | + const text = await page.getByTestId("data-roundtrip-0").textContent(); |
| 168 | + expect(text).toMatch(/Roundtrip (OK|FAIL)/); |
| 169 | + await snapshot(page, "10_new_ui", "roundtrip_badge"); |
| 170 | +}); |
0 commit comments