Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 258 additions & 0 deletions app/e2e/heavy-widgets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import {
test,
expect,
ALICE,
createTestDashboard,
typeInEditor,
getPreview,
} from "./fixtures";

test.describe("Heavy widget rendering", () => {
let dashboardCleanup: (() => Promise<void>) | undefined;

test.beforeEach(async ({ authPage, page }) => {
await authPage.login(ALICE.email, ALICE.password);
const { id, cleanup } = await createTestDashboard(
page.request,
`Heavy Widgets ${Date.now()}`,
);
dashboardCleanup = cleanup;
await page.goto(`/${id}/edit`);
await expect(page.getByText("Editing:")).toBeVisible();
});

test.afterEach(async () => {
await dashboardCleanup?.();
});

// ── Pie ─────────────────────────────────────────────────────────────

test("pie chart — renders canvas and adds to dashboard", async ({ page }) => {
test.setTimeout(60_000);
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Pie Chart" }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(
dialog,
page,
"MATCH ()-[r]->() RETURN type(r) AS name, count(*) AS value",
);
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

const preview = getPreview(dialog);
await expect(preview.locator("canvas")).toBeVisible({ timeout: 15_000 });

Comment on lines +50 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Assert Pie output, not just canvas mount.

A visible canvas only proves the widget mounted. It does not cover the acceptance criteria for correct slice count and legend visibility, so this can still pass when Pie renders the wrong series data.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/e2e/heavy-widgets.spec.ts` around lines 50 - 52, The test currently only
checks that the canvas mounted via getPreview(dialog) and
preview.locator("canvas"), but must also assert the actual Pie output; update
the test that uses getPreview(dialog) to add assertions that the rendered Pie
has the expected number of slices and that the legend items are visible (use
preview.locator(...) with selectors targeting the pie slice elements and legend
entries and assert .toHaveCount(expectedCount) and .toBeVisible() accordingly).
Ensure you reference preview.locator for both the slice selector and the legend
selector so the test fails if the Pie renders wrong series or hides the legend.

// Add to dashboard and verify it renders on the grid
await dialog.getByRole("button", { name: "Add Widget" }).click();
await expect(dialog).not.toBeVisible({ timeout: 10_000 });
await expect(
page.locator("[data-testid='widget-card'] canvas"),
).toBeVisible({ timeout: 15_000 });
});

test("pie chart — scalar query shows incompatible data format", async ({
page,
}) => {
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Pie Chart" }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(dialog, page, "RETURN 42 AS scalar");
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

await expect(dialog.getByText("Incompatible data format")).toBeVisible({
timeout: 10_000,
});
});

// ── Map ─────────────────────────────────────────────────────────────

test("map chart — renders Leaflet container with markers", async ({
page,
}) => {
test.setTimeout(60_000);
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Map", exact: true }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(
dialog,
page,
"UNWIND range(1,5) AS i RETURN 40.0+i AS lat, -73.0+i AS lng, 'Point ' + i AS name",
);
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

const preview = getPreview(dialog);
await expect(preview.locator(".leaflet-container")).toBeVisible({
timeout: 15_000,
});
await expect(
preview.locator(".leaflet-overlay-pane svg").first(),
).toBeVisible({ timeout: 10_000 });
Comment on lines +107 to +113

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Verify plotted markers, not only the Leaflet shell.

.leaflet-container and an overlay svg can exist even if the lat/lng rows were not turned into map markers correctly. Please add an assertion on rendered marker primitives/count for the 5 returned points.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/e2e/heavy-widgets.spec.ts` around lines 107 - 113, The test currently
only checks that the Leaflet container and overlay svg are visible; update the
assertion to verify the actual markers for the 5 returned points by adding an
assertion against the rendered marker primitives (e.g., count of elements
matching the marker selector) from the preview returned by getPreview(dialog):
locate either ".leaflet-marker-icon" (for default Leaflet icons) or the SVG
marker primitives inside ".leaflet-overlay-pane svg" (e.g., "circle" or
".marker" elements) and assert their count equals 5 with an appropriate timeout,
keeping the existing preview and locator usage.


// Add to dashboard and verify Leaflet renders on the grid
await dialog.getByRole("button", { name: "Add Widget" }).click();
await expect(dialog).not.toBeVisible({ timeout: 10_000 });
await expect(
page.locator("[data-testid='widget-card'] .leaflet-container"),
).toBeVisible({ timeout: 15_000 });
});

test("map chart — pan/zoom interaction does not crash", async ({ page }) => {
test.setTimeout(60_000);
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Map", exact: true }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(
dialog,
page,
"UNWIND range(1,5) AS i RETURN 40.0+i AS lat, -73.0+i AS lng, 'Point ' + i AS name",
);
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

const preview = getPreview(dialog);
const mapContainer = preview.locator(".leaflet-container");
await expect(mapContainer).toBeVisible({ timeout: 15_000 });

// Zoom in and out via Leaflet controls — should not crash
const zoomIn = preview.locator(".leaflet-control-zoom-in");
await expect(zoomIn).toBeVisible({ timeout: 5_000 });
await zoomIn.click();
await expect(mapContainer).toBeVisible();

const zoomOut = preview.locator(".leaflet-control-zoom-out");
await zoomOut.click();
await expect(mapContainer).toBeVisible();
Comment on lines +147 to +155

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This only tests zoom, not pan.

The test name and linked objective call out pan/zoom, but this segment only clicks zoom controls. Add a drag interaction on the map surface and assert the map remains functional after the pan.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/e2e/heavy-widgets.spec.ts` around lines 147 - 155, The test currently
exercises only zoom controls (using zoomIn/zoomOut) but must also perform a pan;
add a drag interaction against the map surface (use the existing mapContainer
locator or preview.locator('.leaflet-container') to perform a mouse
drag/locator.dragTo) to simulate panning, then assert the map still renders
(e.g. await expect(mapContainer).toBeVisible() or another existing post-pan
assertion) to prove the map remains functional after the pan.


// Tile pane should still contain loaded tiles
await expect(preview.locator(".leaflet-tile-pane img").first()).toBeVisible(
{ timeout: 10_000 },
);

// No errors should have appeared
await expect(dialog.getByText("Query Failed")).not.toBeVisible();
await expect(
dialog.getByText("Incompatible data format"),
).not.toBeVisible();
});

test("map chart — scalar query shows incompatible data format", async ({
page,
}) => {
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Map", exact: true }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(dialog, page, "RETURN 42 AS scalar");
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

await expect(dialog.getByText("Incompatible data format")).toBeVisible({
timeout: 10_000,
});
});

// ── Graph ───────────────────────────────────────────────────────────

test("graph chart — renders nodes and Fit graph interaction works", async ({
page,
}) => {
test.setTimeout(60_000);
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Graph" }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(
dialog,
page,
"MATCH (p:Person)-[r:ACTED_IN]->(m:Movie) RETURN p, r, m LIMIT 10",
);
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

const preview = getPreview(dialog);
await expect(preview).toBeVisible({ timeout: 15_000 });

// Graph toolbar proves NVL rendered successfully
const fitBtn = dialog.getByRole("button", { name: "Fit graph" });
await expect(fitBtn).toBeVisible({ timeout: 10_000 });

// Click Fit graph — should not crash
await fitBtn.click();
await expect(dialog.getByText("Query Failed")).not.toBeVisible();
Comment on lines +215 to +224

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Toolbar visibility is too weak for Graph coverage.

Fit graph proves the NVL UI mounted, but not that nodes and edges rendered, pan/zoom works, or styling rules apply. Those are explicit acceptance criteria for this PR, so the main Graph regression surface is still untested here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/e2e/heavy-widgets.spec.ts` around lines 215 - 224, The test currently
only verifies toolbar visibility by locating the "Fit graph" button (fitBtn) and
clicking it, which doesn't assert that nodes/edges rendered or pan/zoom/styling
work; update the heavy-widgets.spec.ts test to explicitly check graph rendering
and interactions: after getPreview(dialog) and ensuring preview visible, locate
the graph canvas/SVG element (e.g., query for NVL graph container or role),
assert that a minimum number of node and edge DOM elements are present, verify
styling by checking a node/edge has the expected CSS class or computed style,
and assert pan/zoom by performing a drag or wheel event on the graph element and
confirming node positions change (or transform attribute updates); keep the
existing fitBtn click but add these explicit assertions around getPreview, the
graph container, and node/edge selectors to satisfy the PR acceptance criteria.


// Add to dashboard
await dialog.getByRole("button", { name: "Add Widget" }).click();
await expect(dialog).not.toBeVisible({ timeout: 10_000 });
await expect(page.getByRole("button", { name: "Fit graph" })).toBeVisible({
timeout: 15_000,
});
});

test("graph chart — scalar query shows incompatible data format", async ({
page,
}) => {
await page.getByRole("button", { name: "Add Widget" }).first().click();
const dialog = page.getByRole("dialog", { name: "Add Widget" });

await dialog.getByRole("combobox").nth(1).click();
await page.getByRole("option", { name: "Graph" }).click();
await dialog.getByRole("combobox").nth(0).click();
await page.getByRole("option", { name: /Movies Graph/ }).click();

await typeInEditor(dialog, page, "RETURN 42 AS scalar");
await expect(
dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)"),
).toBeEnabled({ timeout: 10_000 });
await dialog.getByTitle("Run query (Ctrl+Enter / ⌘+Enter)").click();

await expect(dialog.getByText("Incompatible data format")).toBeVisible({
timeout: 10_000,
});
await expect(
dialog.getByRole("button", { name: "Fit graph" }),
).not.toBeVisible();
});
});
Loading