test(e2e): add Playwright smoke for FilesTab split
Walks the real UI end-to-end:
1. Creates + registers a workspace on the platform
2. Opens the detail side panel
3. Clicks the Files tab (force-click since it's in an overflow-x bar)
4. Asserts all 3 split components render:
- FilesToolbar: "+ New" + "Upload" buttons
- FileTree: the config.yaml seeded by the default template
- FileEditor: "Select a file to edit" empty-state
Saves screenshots at /tmp/filestab-{1,2,3}-*.png for manual review.
Run: cd canvas && npx playwright test e2e/filestab-smoke.spec.ts
Requires platform on :8080 + canvas on :3000.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c71cd39ee7
commit
235b4b192b
84
canvas/e2e/filestab-smoke.spec.ts
Normal file
84
canvas/e2e/filestab-smoke.spec.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Smoke test for the PR #10 FilesTab split. Exercises the UI end-to-end:
|
||||
* - creates a workspace on the platform
|
||||
* - opens the detail panel
|
||||
* - switches to the Files tab
|
||||
* - confirms tree, toolbar, and editor panels render (the three extracted
|
||||
* sibling components: FileTree, FilesToolbar, FileEditor)
|
||||
* - saves a screenshot for visual review
|
||||
*
|
||||
* Requires platform on :8080 and canvas on :3000.
|
||||
*/
|
||||
test("FilesTab renders after split", async ({ page, request }) => {
|
||||
// Clean slate
|
||||
const { workspaces } = await request
|
||||
.get("http://localhost:8080/workspaces")
|
||||
.then(async (r) => ({ workspaces: (await r.json()) as Array<{ id: string }> }));
|
||||
for (const w of workspaces) {
|
||||
await request.delete(`http://localhost:8080/workspaces/${w.id}?confirm=true`);
|
||||
}
|
||||
|
||||
// Create a workspace
|
||||
const created = await request
|
||||
.post("http://localhost:8080/workspaces", {
|
||||
data: { name: "FilesTab Smoke", tier: 1, runtime: "langgraph" },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
.then((r) => r.json());
|
||||
const wsId = created.id as string;
|
||||
|
||||
// Register so status flips online (so detail panel content loads cleanly)
|
||||
await request.post("http://localhost:8080/registry/register", {
|
||||
data: { id: wsId, url: "http://localhost:9999", agent_card: { name: "Smoke", skills: [] } },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await expect(page).toHaveTitle(/Molecule AI/);
|
||||
|
||||
// Screenshot: landing
|
||||
await page.screenshot({ path: "/tmp/filestab-1-landing.png", fullPage: false });
|
||||
|
||||
// Dismiss any onboarding overlay if present (best-effort)
|
||||
const skip = page.getByText(/skip guide/i).first();
|
||||
if (await skip.isVisible().catch(() => false)) await skip.click();
|
||||
|
||||
// Click the workspace node — title text is unique
|
||||
const node = page.getByText("FilesTab Smoke").first();
|
||||
await node.waitFor({ timeout: 10_000 });
|
||||
await node.click();
|
||||
|
||||
// Side panel should open
|
||||
await page.waitForTimeout(300);
|
||||
await page.screenshot({ path: "/tmp/filestab-2-panel.png", fullPage: false });
|
||||
|
||||
// Switch to Files tab. The tab bar overflows-x and buttons off-screen
|
||||
// resist the usual click path. Use Playwright's force-click on the
|
||||
// hidden button; this fires a real React onClick.
|
||||
// Tab button text is "⊞ Files" (icon + label). Use hasText substring.
|
||||
const filesBtn = page.locator("button").filter({ hasText: "Files" });
|
||||
await filesBtn.first().scrollIntoViewIfNeeded();
|
||||
await filesBtn.first().click({ force: true });
|
||||
|
||||
await page.waitForTimeout(1200); // let files API load + render the 3 split components
|
||||
await page.screenshot({ path: "/tmp/filestab-3-files.png", fullPage: false });
|
||||
|
||||
// Hard assertion: all three split components are visible.
|
||||
// FilesToolbar: "+ New", "Upload", "Export", "Clear" buttons.
|
||||
// FileTree: the config.yaml file from the Go provisioner's default template.
|
||||
// FileEditor: the empty-state placeholder "Select a file to edit".
|
||||
const toolbarNew = page.getByRole("button", { name: /new/i });
|
||||
const toolbarUpload = page.getByRole("button", { name: /upload/i });
|
||||
const treeFile = page.getByText("config.yaml");
|
||||
const editorEmpty = page.getByText(/select a file/i);
|
||||
|
||||
await expect(toolbarNew.first()).toBeVisible({ timeout: 5_000 });
|
||||
await expect(toolbarUpload.first()).toBeVisible({ timeout: 5_000 });
|
||||
await expect(treeFile.first()).toBeVisible({ timeout: 5_000 });
|
||||
await expect(editorEmpty.first()).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
// Cleanup
|
||||
await request.delete(`http://localhost:8080/workspaces/${wsId}?confirm=true`);
|
||||
});
|
||||
65
canvas/package-lock.json
generated
65
canvas/package-lock.json
generated
@ -24,6 +24,7 @@
|
||||
"zustand": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.59.1",
|
||||
"@testing-library/jest-dom": "^6.6.0",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/node": "^22.0.0",
|
||||
@ -488,6 +489,23 @@
|
||||
"url": "https://github.com/sponsors/Boshen"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
|
||||
"integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
@ -3885,6 +3903,53 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
|
||||
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
|
||||
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.8",
|
||||
"funding": [
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"zustand": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.59.1",
|
||||
"@testing-library/jest-dom": "^6.6.0",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/node": "^22.0.0",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user