Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit e0676e48592b6c6879a5a034a3e7be1d7f3fb0aa
parent 0e59ad100944b060f3942fc36028263249a0fdf5
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Wed, 30 Sep 2026 17:44:18 -0400

export(e2e): search-in after the review — a "Posts" leaf with Posts unticked, a tag chip finishes, no video type kept, the refusal described

search-in.spec: case 9 now also runs a "Posts" leaf with Transcripts and
Posts unticked (L3, M2); a plain query under a tag chip finishes with its
results, and with posts alone says "No matching videos." (M1); with Videos
and Livestreams unticked a plain query reads the posts and finishes (M1);
the refusal line is always there and empty, the disabled Search button is
described by it, and Save as is disabled with it (L1, L2). posts-search:
unticking Posts leaves the "Posts" leaf's two posts (M2).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mexport/e2e/posts-search.spec.ts | 8+++++---
Mexport/e2e/search-in.spec.ts | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/export/e2e/posts-search.spec.ts b/export/e2e/posts-search.spec.ts @@ -162,7 +162,7 @@ test.describe("social-post corpus — search", () => { ).toBeVisible(); }); - test("the Posts box switches the corpus off", async ({ page }) => { + test("the Posts box leaves a \"Posts\" leaf reading posts", async ({ page }) => { const tree: SGroup = { k: "g", o: "OR", @@ -181,8 +181,8 @@ test.describe("social-post corpus — search", () => { ]); // The Posts box sits under Search in (release 16; it was in the Type row, - // same key `nop`). Unticked, it also keeps the posts corpus out of the - // scope a "Posts" leaf reads, as it always has. + // same key `nop`) and says what a plain query reads. A leaf whose scope is + // "Posts" was asked for by name, and still reads its two posts unticked. await page.getByRole("checkbox", { name: "Posts" }).uncheck(); await page.getByTestId("search-submit").click(); @@ -190,6 +190,8 @@ test.describe("social-post corpus — search", () => { TRANSCRIPT_ONLY_SLUG, CHAT_SMALL_SLUG, CHAT_LARGE_SLUG, + POST_ROOT_SLUG, + POST_REPLY_SLUG, ]); }); }); diff --git a/export/e2e/search-in.spec.ts b/export/e2e/search-in.spec.ts @@ -1,6 +1,7 @@ import { expect, test, type Page } from "@playwright/test"; import { CHANNEL_SLUG, + TAG_COLLAB, POST_CHANNEL_SLUG, POST_REPLY_ID, POST_ROOT_ID, @@ -8,7 +9,7 @@ import { VIDEO_CHAT_SMALL, VIDEO_TRANSCRIPT_ONLY, } from "./fixtures/data"; -import { installRoutes, openFilters, showAll } from "./helpers"; +import { installRoutes, installTagRoutes, openFilters, showAll } from "./helpers"; // Release 16, slice CK: the Filters panel's "Search in" row — Transcripts, // Posts, Live chat — says what a plain query (a "transcripts" leaf) reads. @@ -52,6 +53,11 @@ async function search(page: Page, q: string) { await page.getByTestId("search-submit").click(); } +// The search has finished: the progress line has lost its ellipsis. +async function expectFinished(page: Page) { + await expect(page.getByText(/^searched \d+\/\d+$/)).toBeVisible({ timeout: 15_000 }); +} + test.describe("Search in", () => { test.beforeEach(async ({ page }) => { await installRoutes(page); @@ -74,7 +80,8 @@ test.describe("Search in", () => { await expect(hint).toBeVisible(); await box(page, "Live chat").check(); await expect(hint).toHaveCount(0); - await expect(page.getByTestId("search-in-empty")).toHaveCount(0); + // The refusal line is always there, and empty unless Search is refused. + await expect(page.getByTestId("search-in-empty")).toBeEmpty(); }); test("a plain query reads posts by default; unticking Posts drops them", async ({ @@ -175,6 +182,11 @@ test.describe("Search in", () => { const empty = page.getByTestId("search-in-empty"); await expect(submit).toBeDisabled(); await expect(empty).toHaveText("Search in: pick at least one"); + await expect(empty).toHaveAttribute("role", "status"); + // The disabled button says why, to a screen reader as well as on hover. + await expect(submit).toHaveAccessibleDescription("Search in: pick at least one"); + // A save commits the draft, so Save as… is refused the same way. + await expect(page.getByRole("button", { name: "Save as…" })).toBeDisabled(); await expect(page.getByText("Press Enter or click Search to apply")).toHaveCount(0); // Enter does not commit it either. await leafInput(page).press("Enter"); @@ -184,7 +196,9 @@ test.describe("Search in", () => { await box(page, "Live chat").check(); await expect(submit).toBeEnabled(); - await expect(empty).toHaveCount(0); + await expect(empty).toBeEmpty(); + await expect(submit).toHaveAccessibleDescription(""); + await expect(page.getByRole("button", { name: "Save as…" })).toBeEnabled(); await box(page, "Live chat").uncheck(); await expect(submit).toBeDisabled(); await box(page, "Posts").check(); @@ -256,11 +270,58 @@ test.describe("Search in", () => { test("a leaf asked for by name is not changed by the row", async ({ page }) => { // A "Live chat" leaf reads the chat with the row at its default (Live - // chat unticked), and a "Posts" leaf reads posts with Transcripts off. + // chat unticked)… const chatLeaf = { k: "g", o: "AND", c: [{ k: "l", q: "message", s: "chat" }] }; await page.goto(`/?qt=${encodeURIComponent(JSON.stringify(chatLeaf))}`); await expectResultSlugs(page, [CHAT_SMALL_SLUG]); await openFilters(page); await expect(box(page, "Live chat")).not.toBeChecked(); + + // …and a "Posts" leaf reads posts with Transcripts and Posts unticked + // (Live chat ticked, so the row reads something and can be committed). + await box(page, "Transcripts").uncheck(); + await box(page, "Posts").uncheck(); + await box(page, "Live chat").check(); + await page.getByTestId("search-submit").click(); + const postsLeaf = { k: "g", o: "AND", c: [{ k: "l", q: "kappa", s: "posts" }] }; + await page.goto(`/?qt=${encodeURIComponent(JSON.stringify(postsLeaf))}`); + await openFilters(page); + await expect(box(page, "Transcripts")).not.toBeChecked(); + await expect(box(page, "Posts")).not.toBeChecked(); + await expectResultSlugs(page, POST_SLUGS); + }); + + test("a plain query with a tag chip on finishes, with its results or none", async ({ + page, + }) => { + // Posts carry no curated tags, so under a chip the plain query reads no + // posts; before the fix the posts copy it read had an empty scope and the + // search never finished. + await installTagRoutes(page); + await page.goto("/"); + await openFilters(page); + await page.locator(`[data-testid="tag-chip"][data-tag-id="${TAG_COLLAB}"]`).click(); + await search(page, "alpha"); + await expectResultSlugs(page, [CHAT_SMALL_SLUG, CHAT_LARGE_SLUG]); + await expectFinished(page); + + // Posts alone under a chip: nothing can match, and it says so. + await box(page, "Transcripts").uncheck(); + await search(page, "kappa"); + await expect(page.getByText("No matching videos.")).toBeVisible({ timeout: 15_000 }); + await expectFinished(page); + }); + + test("with no video type kept, a plain query still reads the posts, and finishes", async ({ + page, + }) => { + // The transcripts copy's scope is then empty (the Type row keeps no video). + await page.goto("/"); + await openFilters(page); + await page.getByRole("checkbox", { name: "Videos" }).uncheck(); + await page.getByRole("checkbox", { name: "Livestreams" }).uncheck(); + await search(page, "kappa"); + await expectResultSlugs(page, POST_SLUGS); + await expectFinished(page); }); });