fix: regenerate E2E screenshots for 2-tier feature system

- Rewrote screenshot-feature-flags.ts: removed stale 'unstable' tier
  references (relay-members, identity-archive), updated close button
  selector to settings-back-to-app
- Deleted all old screenshots + feature-flags-tour/ artifacts
- Regenerated 13 fresh screenshots matching current UI
- Added Experiments nav group to SettingsView (was defined but missing
  from settingsNavGroups)
- Registered test in playwright.config.ts smoke project

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-06-08 13:02:58 -07:00
parent 185d94db61
commit 7194016d48
43 changed files with 90 additions and 61 deletions
+1
View File
@@ -35,6 +35,7 @@ export default defineConfig({
"**/workflows.spec.ts",
"**/identity-archive.spec.ts",
"**/identity-archive-hide.spec.ts",
"**/screenshot-feature-flags.ts",
],
use: {
...devices["Desktop Chrome"],
@@ -25,9 +25,7 @@ function FeatureRow({ feature }: { feature: FeatureDefinition }) {
}
export function ExperimentalFeaturesCard() {
const previewFeatures = desktopFeatures.filter(
(f) => f.tier === "preview",
);
const previewFeatures = desktopFeatures.filter((f) => f.tier === "preview");
return (
<section className="min-w-0" data-testid="settings-experimental">
@@ -64,6 +64,10 @@ const settingsNavGroups: Array<{
label: "App",
sections: ["agents", "compute", "mobile", "updates", "doctor"],
},
{
label: "Experiments",
sections: ["experimental"],
},
];
function SettingsSectionButton({
@@ -127,7 +131,10 @@ export function SettingsView({
// Feature gate check
if (s.featureGate) {
const feature = getFeature(s.featureGate);
if (feature && !resolveEnabled(feature.tier, feature.id, featureState)) {
if (
feature &&
!resolveEnabled(feature.tier, feature.id, featureState)
) {
return false;
}
}
@@ -7,7 +7,10 @@ describe("resolveEnabled", () => {
describe("stable tier", () => {
it("always returns true regardless of overrides", () => {
assert.equal(resolveEnabled("stable", "channels", {}), true);
assert.equal(resolveEnabled("stable", "channels", { channels: false }), true);
assert.equal(
resolveEnabled("stable", "channels", { channels: false }),
true,
);
});
});
+4 -1
View File
@@ -6,7 +6,10 @@ const srcRoot = path.resolve(
"src",
);
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const repoRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"..",
);
export function resolve(specifier, context, nextResolve) {
if (specifier === "@features-manifest") {
+72 -55
View File
@@ -9,20 +9,23 @@ test.beforeEach(async ({ page }) => {
await installMockBridge(page);
});
// Helper: enable a feature via settings, then close settings
async function enableFeature(page: import("@playwright/test").Page, featureId: string) {
// Helper: enable a preview feature via settings, then close settings
async function enableFeature(
page: import("@playwright/test").Page,
featureId: string,
) {
await openSettings(page);
await page.getByTestId("settings-nav-experimental").click();
await expect(page.getByTestId("settings-experimental")).toBeVisible();
await page.getByTestId(`feature-toggle-${featureId}`).click();
await page.waitForTimeout(200);
await page.getByTestId("settings-close").click();
await page.getByTestId("settings-back-to-app").click();
await page.waitForTimeout(300);
}
// --- Stable features (settings panels) ---
// --- Stable features (settings panels, always visible) ---
test("screenshot: Settings → Agents (managed-agents, stable)", async ({ page }) => {
test("screenshot: Settings → Agents (stable)", async ({ page }) => {
await page.goto("/");
await openSettings(page, "agents");
await page.waitForTimeout(500);
@@ -30,24 +33,28 @@ test("screenshot: Settings → Agents (managed-agents, stable)", async ({ page }
await view.screenshot({ path: "tests/e2e/screenshots/settings-agents.png" });
});
test("screenshot: Settings → Templates (channel-templates, stable)", async ({ page }) => {
test("screenshot: Settings → Templates (stable)", async ({ page }) => {
await page.goto("/");
await openSettings(page, "channel-templates");
await page.waitForTimeout(500);
const view = page.getByTestId("settings-view");
await view.screenshot({ path: "tests/e2e/screenshots/settings-templates.png" });
await view.screenshot({
path: "tests/e2e/screenshots/settings-templates.png",
});
});
test("screenshot: Settings → Custom Emoji (custom-emoji, stable)", async ({ page }) => {
test("screenshot: Settings → Custom Emoji (stable)", async ({ page }) => {
await page.goto("/");
await openSettings(page);
await page.getByTestId("settings-nav-custom-emoji").click();
await page.waitForTimeout(500);
const view = page.getByTestId("settings-view");
await view.screenshot({ path: "tests/e2e/screenshots/settings-custom-emoji.png" });
await view.screenshot({
path: "tests/e2e/screenshots/settings-custom-emoji.png",
});
});
test("screenshot: Settings → Doctor (doctor, stable)", async ({ page }) => {
test("screenshot: Settings → Doctor (stable)", async ({ page }) => {
await page.goto("/");
await openSettings(page, "doctor");
await page.waitForTimeout(500);
@@ -55,64 +62,84 @@ test("screenshot: Settings → Doctor (doctor, stable)", async ({ page }) => {
await view.screenshot({ path: "tests/e2e/screenshots/settings-doctor.png" });
});
// --- Preview features (navigated into their views) ---
// --- Preview features (opt-in via Experiments) ---
test("screenshot: Workflows view (workflows, preview)", async ({ page }) => {
test("screenshot: Workflows view (preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "workflows");
await page.getByTestId("open-workflows-view").click();
await page.waitForTimeout(500);
await page.screenshot({ path: "tests/e2e/screenshots/view-workflows.png", fullPage: false });
await page.screenshot({
path: "tests/e2e/screenshots/view-workflows.png",
fullPage: false,
});
});
test("screenshot: Projects view (projects, preview)", async ({ page }) => {
test("screenshot: Projects view (preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "projects");
await page.getByTestId("open-projects-view").click();
await page.waitForTimeout(500);
await page.screenshot({ path: "tests/e2e/screenshots/view-projects.png", fullPage: false });
await page.screenshot({
path: "tests/e2e/screenshots/view-projects.png",
fullPage: false,
});
});
test("screenshot: Pulse view (pulse, preview)", async ({ page }) => {
test("screenshot: Pulse view (preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "pulse");
await page.getByTestId("open-pulse-view").click();
await page.waitForTimeout(500);
await page.screenshot({ path: "tests/e2e/screenshots/view-pulse.png", fullPage: false });
await page.screenshot({
path: "tests/e2e/screenshots/view-pulse.png",
fullPage: false,
});
});
test("screenshot: Forum with active thread (forum, preview)", async ({ page }) => {
test("screenshot: Forum post list (preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "forum");
// Navigate directly into the forum channel and open a thread
await page.goto(`/#/channels/${WATERCOLOR_CHANNEL_ID}/posts/${FORUM_POST_ID}`);
await page.goto(`/#/channels/${WATERCOLOR_CHANNEL_ID}`);
await expect(
page.getByText("Release checklist: async feedback thread."),
).toBeVisible();
await page.waitForTimeout(500);
await page.screenshot({
path: "tests/e2e/screenshots/view-forum-posts.png",
fullPage: false,
});
});
test("screenshot: Forum with active thread (preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "forum");
await page.goto(
`/#/channels/${WATERCOLOR_CHANNEL_ID}/posts/${FORUM_POST_ID}`,
);
await expect(page.getByTestId("chat-title")).toHaveText("watercooler");
await page.waitForTimeout(600);
await page.screenshot({ path: "tests/e2e/screenshots/view-forum-thread.png", fullPage: false });
await page.screenshot({
path: "tests/e2e/screenshots/view-forum-thread.png",
fullPage: false,
});
});
test("screenshot: Forum post list (forum, preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "forum");
// Navigate to the forum channel post list
await page.goto(`/#/channels/${WATERCOLOR_CHANNEL_ID}`);
await expect(page.getByText("Release checklist: async feedback thread.")).toBeVisible();
await page.waitForTimeout(500);
await page.screenshot({ path: "tests/e2e/screenshots/view-forum-posts.png", fullPage: false });
});
test("screenshot: Huddles in channel header (huddles, preview)", async ({ page }) => {
test("screenshot: Huddles in channel header (preview)", async ({ page }) => {
await page.goto("/");
await enableFeature(page, "huddles");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
await page.waitForTimeout(500);
await page.screenshot({ path: "tests/e2e/screenshots/view-huddles.png", fullPage: false });
await page.screenshot({
path: "tests/e2e/screenshots/view-huddles.png",
fullPage: false,
});
});
// --- Unstable features (settings panels after enabling) ---
test("screenshot: Settings → Compute (mesh-compute, unstable)", async ({ page }) => {
test("screenshot: Settings → Compute (preview, after enabling)", async ({
page,
}) => {
await page.goto("/");
await openSettings(page);
await page.getByTestId("settings-nav-experimental").click();
@@ -124,31 +151,21 @@ test("screenshot: Settings → Compute (mesh-compute, unstable)", async ({ page
await view.screenshot({ path: "tests/e2e/screenshots/settings-compute.png" });
});
test("screenshot: Settings → Relay Members (relay-members, unstable)", async ({ page }) => {
await page.goto("/");
await openSettings(page);
await page.getByTestId("settings-nav-experimental").click();
await page.getByTestId("feature-toggle-relay-members").click();
await page.waitForTimeout(300);
await page.getByTestId("settings-nav-relay-members").click();
await page.waitForTimeout(500);
const view = page.getByTestId("settings-view");
await view.screenshot({ path: "tests/e2e/screenshots/settings-relay-members.png" });
});
// --- Experiments panel ---
// --- Settings → Preview & Unstable panel ---
test("screenshot: Settings → Preview & Unstable (default)", async ({ page }) => {
test("screenshot: Settings → Experiments (default)", async ({ page }) => {
await page.goto("/");
await openSettings(page);
await page.getByTestId("settings-nav-experimental").click();
await expect(page.getByTestId("settings-experimental")).toBeVisible();
await page.waitForTimeout(500);
const view = page.getByTestId("settings-view");
await view.screenshot({ path: "tests/e2e/screenshots/settings-preview-unstable-default.png" });
await view.screenshot({
path: "tests/e2e/screenshots/settings-experiments-default.png",
});
});
test("screenshot: Settings → Preview & Unstable (all on)", async ({ page }) => {
test("screenshot: Settings → Experiments (all on)", async ({ page }) => {
await page.goto("/");
await openSettings(page);
await page.getByTestId("settings-nav-experimental").click();
@@ -159,9 +176,9 @@ test("screenshot: Settings → Preview & Unstable (all on)", async ({ page }) =>
await page.getByTestId("feature-toggle-forum").click();
await page.getByTestId("feature-toggle-huddles").click();
await page.getByTestId("feature-toggle-mesh-compute").click();
await page.getByTestId("feature-toggle-identity-archive").click();
await page.getByTestId("feature-toggle-relay-members").click();
await page.waitForTimeout(500);
const view = page.getByTestId("settings-view");
await view.screenshot({ path: "tests/e2e/screenshots/settings-preview-unstable-all-on.png" });
await view.screenshot({
path: "tests/e2e/screenshots/settings-experiments-all-on.png",
});
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB