-
Notifications
You must be signed in to change notification settings - Fork 0
test(widgets): E2E Widget Lab consumption — duplicate, filter, search (#489) #523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -729,4 +729,127 @@ test.describe("Widget Lab", () => { | |
| ).not.toBeVisible(); | ||
| }); | ||
| }); | ||
|
|
||
| // ── Widget Lab consumption: duplicate, filter, search ─────────────── | ||
|
|
||
| test.describe("Widget Lab consumption", () => { | ||
| let templateIds: string[] = []; | ||
|
|
||
| test.beforeEach(async ({ page }) => { | ||
| // Create two templates via API for filter/search tests | ||
| const neo4jBar = await page.request.post("/api/widget-templates", { | ||
| data: { | ||
| name: "Neo4j Bar Template", | ||
| chartType: "bar", | ||
| connectorType: "neo4j", | ||
| query: "MATCH (m:Movie) RETURN m.title AS label, count(*) AS value", | ||
| }, | ||
| }); | ||
| const pgTable = await page.request.post("/api/widget-templates", { | ||
| data: { | ||
| name: "PostgreSQL Table Template", | ||
| chartType: "table", | ||
| connectorType: "postgresql", | ||
| query: "SELECT title FROM movies LIMIT 5", | ||
| }, | ||
| }); | ||
| const t1 = (await neo4jBar.json()).data; | ||
| const t2 = (await pgTable.json()).data; | ||
| templateIds = [t1.id, t2.id]; | ||
| }); | ||
|
|
||
| test.afterEach(async ({ page }) => { | ||
| for (const id of templateIds) { | ||
| await page.request.delete(`/api/widget-templates/${id}`); | ||
| } | ||
| templateIds = []; | ||
| }); | ||
|
|
||
| test("can duplicate a template", async ({ page }) => { | ||
| await page.goto("/widget-lab"); | ||
| const card = page | ||
| .locator("[data-testid='template-card']") | ||
| .filter({ hasText: "Neo4j Bar Template" }) | ||
| .first(); | ||
| await expect(card).toBeVisible({ timeout: 10_000 }); | ||
|
|
||
| await card.getByLabel("Duplicate").click(); | ||
|
|
||
| // Duplicate should appear with "(copy)" suffix | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template (copy)", { exact: true }), | ||
| ).toBeVisible({ timeout: 10_000 }); | ||
|
|
||
| // Clean up the duplicate | ||
| const res = await page.request.get("/api/widget-templates"); | ||
| const templates = (await res.json()).data; | ||
| const copy = templates.find( | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| (t: any) => t.name === "Neo4j Bar Template (copy)", | ||
| ); | ||
|
Comment on lines
+786
to
+789
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Check explicit any usage in the changed test file:"
rg -nP '\bany\b' app/e2e/widget-lab.spec.ts
echo
echo "Check TS strict/noImplicitAny config under app/:"
fd -i '^tsconfig.*\.json$' app --exec rg -n '"strict"\s*:\s*true|"noImplicitAny"\s*:\s*true' {}Repository: alfredo1996/neoboard Length of output: 683 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Check full context of the template lookup at lines 676-679 ==="
sed -n '670,690p' app/e2e/widget-lab.spec.ts
echo
echo "=== Check the /api/widget-templates endpoint to understand response structure ==="
fd -t f '\.(ts|tsx)$' app --exec grep -l 'widget-templates' {} \;
echo
echo "=== Check all occurrences of the template pattern to see if they're all identical ==="
sed -n '126,140p' app/e2e/widget-lab.spec.ts
echo "---"
sed -n '350,365p' app/e2e/widget-lab.spec.tsRepository: alfredo1996/neoboard Length of output: 1537 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Check the widget-templates API route ==="
cat -n app/src/app/api/widget-templates/route.ts | head -80
echo
echo "=== Check hook that uses widget-templates ==="
cat -n app/src/hooks/use-widget-templates.ts
echo
echo "=== Check if there's a types file for templates ==="
fd -t f 'template' app/src --type f | grep -E '\.(ts|tsx)$' | head -20Repository: alfredo1996/neoboard Length of output: 7305 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Check apiList response structure ==="
grep -A 20 'export function apiList' app/src/lib/api/api-response.ts
echo
echo "=== Check unwrapResponse ==="
grep -A 15 'export.*unwrapResponse' app/src/lib/api/api-client.ts
echo
echo "=== Check WidgetTemplate type ==="
grep -B 5 -A 20 'export.*WidgetTemplate' app/src/lib/db/schema.ts | head -50Repository: alfredo1996/neoboard Length of output: 2372 Remove Line 678 uses type WidgetTemplateListResponse = {
data: Array<{ id: string; name: string }>;
};
const templates = ((await res.json()) as WidgetTemplateListResponse).data;
const copy = templates.find(
(t) => t.name === "Neo4j Bar Template (copy)",
);This eliminates the need for 🤖 Prompt for AI Agents |
||
| if (copy) templateIds.push(copy.id); | ||
| }); | ||
|
|
||
| test("can filter templates by chart type", async ({ page }) => { | ||
| await page.goto("/widget-lab"); | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).toBeVisible({ timeout: 10_000 }); | ||
| await expect( | ||
| page.getByText("PostgreSQL Table Template", { exact: true }), | ||
| ).toBeVisible(); | ||
|
|
||
| // Filter to bar charts only — shadcn Select uses combobox role | ||
| await page.locator("button[role='combobox']").nth(0).click(); | ||
| await page.getByRole("option", { name: "Bar Chart" }).click(); | ||
|
|
||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).toBeVisible(); | ||
| await expect( | ||
| page.getByText("PostgreSQL Table Template", { exact: true }), | ||
| ).not.toBeVisible(); | ||
| }); | ||
|
|
||
| test("can filter templates by connector type", async ({ page }) => { | ||
| await page.goto("/widget-lab"); | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).toBeVisible({ timeout: 10_000 }); | ||
|
|
||
| // Filter to PostgreSQL only — connector select is the second combobox | ||
| await page.locator("button[role='combobox']").nth(1).click(); | ||
| await page.getByRole("option", { name: /PostgreSQL/i }).click(); | ||
|
|
||
| await expect( | ||
| page.getByText("PostgreSQL Table Template", { exact: true }), | ||
| ).toBeVisible(); | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).not.toBeVisible(); | ||
| }); | ||
|
|
||
| test("can search templates by name", async ({ page }) => { | ||
| await page.goto("/widget-lab"); | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).toBeVisible({ timeout: 10_000 }); | ||
|
|
||
| // Search for "PostgreSQL" | ||
| await page.getByPlaceholder("Search templates...").fill("PostgreSQL"); | ||
|
|
||
| await expect( | ||
| page.getByText("PostgreSQL Table Template", { exact: true }), | ||
| ).toBeVisible(); | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).not.toBeVisible(); | ||
|
|
||
| // Clear search | ||
| await page.getByPlaceholder("Search templates...").clear(); | ||
| await expect( | ||
| page.getByText("Neo4j Bar Template", { exact: true }), | ||
| ).toBeVisible(); | ||
| }); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make fixture data unique and assert API setup/cleanup responses.
Line 632 and Line 640 use fixed names, which can collide with stale/retry data and make duplicate/filter assertions ambiguous. Also, setup/teardown API calls (Line 630-Line 645, Line 653) should assert success to fail fast when fixtures don’t load/clean correctly.
Suggested hardening diff
test.describe("Widget Lab consumption", () => { let templateIds: string[] = []; + let neo4jTemplateName = ""; + let pgTemplateName = ""; test.beforeEach(async ({ page }) => { + const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + neo4jTemplateName = `Neo4j Bar Template ${suffix}`; + pgTemplateName = `PostgreSQL Table Template ${suffix}`; + // Create two templates via API for filter/search tests const neo4jBar = await page.request.post("/api/widget-templates", { data: { - name: "Neo4j Bar Template", + name: neo4jTemplateName, chartType: "bar", connectorType: "neo4j", query: "MATCH (m:Movie) RETURN m.title AS label, count(*) AS value", }, }); + expect(neo4jBar.ok()).toBeTruthy(); const pgTable = await page.request.post("/api/widget-templates", { data: { - name: "PostgreSQL Table Template", + name: pgTemplateName, chartType: "table", connectorType: "postgresql", query: "SELECT title FROM movies LIMIT 5", }, }); + expect(pgTable.ok()).toBeTruthy(); const t1 = (await neo4jBar.json()).data; const t2 = (await pgTable.json()).data; templateIds = [t1.id, t2.id]; }); test.afterEach(async ({ page }) => { for (const id of templateIds) { - await page.request.delete(`/api/widget-templates/${id}`); + const delRes = await page.request.delete(`/api/widget-templates/${id}`); + expect(delRes.ok()).toBeTruthy(); } templateIds = []; });🤖 Prompt for AI Agents