test(widgets): E2E Widget Lab consumption — duplicate, filter, search (#489) - #523
Conversation
WalkthroughAdds a new E2E test suite for Widget Lab consumption that creates two templates via API in beforeEach, removes them in afterEach, and exercises duplicating a template, filtering by chart type, filtering by connector type, and searching templates by name. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/e2e/widget-lab.spec.ts`:
- Around line 628-656: The test setup in test.beforeEach uses fixed template
names and does not assert API responses; update the POST calls
(page.request.post in test.beforeEach) to create unique names (e.g., append a
timestamp or random suffix) so fixtures cannot collide across runs, capture and
assert the response status/body for both neo4jBar and pgTable (verify success
and extract IDs into templateIds), and in test.afterEach assert each DELETE
(page.request.delete) returns a successful status before clearing templateIds;
reference the test.beforeEach/test.afterEach blocks, the templateIds variable,
and the page.request.post/page.request.delete calls when making these changes.
- Around line 676-679: Replace the use of any by declaring an explicit response
type for the API result: introduce a type (e.g., WidgetTemplateListResponse with
data: Array<{ id: string; name: string }>) and cast the result of res.json() to
that type when assigning templates, then use templates.find((t) => t.name ===
"Neo4j Bar Template (copy)") to locate copy; update references to res.json(),
templates and copy so the eslint disable and any are removed and strict
TypeScript is preserved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ee3cb65d-684d-495e-8b94-c626102fc5d8
📒 Files selected for processing (1)
app/e2e/widget-lab.spec.ts
| 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 = []; | ||
| }); |
There was a problem hiding this comment.
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
Verify each finding against the current code and only fix it if needed.
In `@app/e2e/widget-lab.spec.ts` around lines 628 - 656, The test setup in
test.beforeEach uses fixed template names and does not assert API responses;
update the POST calls (page.request.post in test.beforeEach) to create unique
names (e.g., append a timestamp or random suffix) so fixtures cannot collide
across runs, capture and assert the response status/body for both neo4jBar and
pgTable (verify success and extract IDs into templateIds), and in test.afterEach
assert each DELETE (page.request.delete) returns a successful status before
clearing templateIds; reference the test.beforeEach/test.afterEach blocks, the
templateIds variable, and the page.request.post/page.request.delete calls when
making these changes.
| const copy = templates.find( | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| (t: any) => t.name === "Neo4j Bar Template (copy)", | ||
| ); |
There was a problem hiding this comment.
🧩 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 any and properly type the API response to maintain strict TypeScript.
Line 678 uses any, which violates strict TS mode. The /api/widget-templates endpoint returns a structured envelope with { data, error, meta }, so the response type can be declared explicitly:
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 @typescript-eslint/no-explicit-any and strengthens type safety.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/e2e/widget-lab.spec.ts` around lines 676 - 679, Replace the use of any by
declaring an explicit response type for the API result: introduce a type (e.g.,
WidgetTemplateListResponse with data: Array<{ id: string; name: string }>) and
cast the result of res.json() to that type when assigning templates, then use
templates.find((t) => t.name === "Neo4j Bar Template (copy)") to locate copy;
update references to res.json(), templates and copy so the eslint disable and
any are removed and strict TypeScript is preserved.
56e1053 to
5ef8842
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (2)
app/e2e/widget-lab.spec.ts (2)
738-766:⚠️ Potential issue | 🟠 MajorHarden fixture setup/teardown to avoid flaky collisions.
The fixed template names and missing API status assertions make this suite brittle on retries/reruns and can hide setup failures.
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
Verify each finding against the current code and only fix it if needed. In `@app/e2e/widget-lab.spec.ts` around lines 738 - 766, The fixture setup/teardown uses fixed template names and doesn't assert API responses, causing flaky collisions; update test.beforeEach to POST templates with unique names (e.g., append timestamp/UUID) when calling page.request.post("/api/widget-templates"), assert the response status (expect 201 or success) and extract ids into templateIds, and make test.afterEach robust by iterating templateIds to DELETE `/api/widget-templates/${id}` while tolerating 404s and ensuring cleanup runs even if setup partially failed; reference the test.beforeEach/test.afterEach blocks and the templateIds variable when making these changes.
786-789:⚠️ Potential issue | 🟡 MinorReplace
anyin template lookup to preserve strict TS.Use a small response type for
/api/widget-templatesand remove the eslint suppression.Typed fix
+ type WidgetTemplateListResponse = { + data: Array<{ id: string; name: string }>; + }; const res = await page.request.get("/api/widget-templates"); - const templates = (await res.json()).data; + const templates = ((await res.json()) as WidgetTemplateListResponse).data; const copy = templates.find( - // eslint-disable-next-line `@typescript-eslint/no-explicit-any` - (t: any) => t.name === "Neo4j Bar Template (copy)", + (t) => t.name === "Neo4j Bar Template (copy)", );As per coding guidelines: "
**/*.{ts,tsx}: TypeScript must be strict. Noanywithout a comment explaining why."#!/bin/bash set -euo pipefail rg -nP '\bany\b|no-explicit-any' app/e2e/widget-lab.spec.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/e2e/widget-lab.spec.ts` around lines 786 - 789, The lookup uses an any type and an eslint-disable; define a small response type (e.g., interface TemplateBrief { name: string; id?: string; /* add fields actually used */ }) for the /api/widget-templates response, type the variable holding templates as TemplateBrief[], remove the eslint-disable comment, and update the find call to templates.find((t: TemplateBrief) => t.name === "Neo4j Bar Template (copy)"); ensure the fetch/parser of the API in this spec uses that TemplateBrief type so TS strict mode is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@app/e2e/widget-lab.spec.ts`:
- Around line 738-766: The fixture setup/teardown uses fixed template names and
doesn't assert API responses, causing flaky collisions; update test.beforeEach
to POST templates with unique names (e.g., append timestamp/UUID) when calling
page.request.post("/api/widget-templates"), assert the response status (expect
201 or success) and extract ids into templateIds, and make test.afterEach robust
by iterating templateIds to DELETE `/api/widget-templates/${id}` while
tolerating 404s and ensuring cleanup runs even if setup partially failed;
reference the test.beforeEach/test.afterEach blocks and the templateIds variable
when making these changes.
- Around line 786-789: The lookup uses an any type and an eslint-disable; define
a small response type (e.g., interface TemplateBrief { name: string; id?:
string; /* add fields actually used */ }) for the /api/widget-templates
response, type the variable holding templates as TemplateBrief[], remove the
eslint-disable comment, and update the find call to templates.find((t:
TemplateBrief) => t.name === "Neo4j Bar Template (copy)"); ensure the
fetch/parser of the API in this spec uses that TemplateBrief type so TS strict
mode is preserved.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: af3699ee-b66d-47ae-93e6-aee527fa9fa2
📒 Files selected for processing (1)
app/e2e/widget-lab.spec.ts
|



Summary
widget-lab.spec.tsfor template consumption flowsCloses #489
Test plan
npx playwright test widget-lab --grep "consumption"— 4/4 passing🤖 Generated with Claude Code
Summary by CodeRabbit