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"
|
"zustand": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.59.1",
|
||||||
"@testing-library/jest-dom": "^6.6.0",
|
"@testing-library/jest-dom": "^6.6.0",
|
||||||
"@testing-library/react": "^16.1.0",
|
"@testing-library/react": "^16.1.0",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
@ -488,6 +489,23 @@
|
|||||||
"url": "https://github.com/sponsors/Boshen"
|
"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": {
|
"node_modules/@radix-ui/primitive": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
@ -3885,6 +3903,53 @@
|
|||||||
"node": ">= 6"
|
"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": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.8",
|
"version": "8.5.8",
|
||||||
"funding": [
|
"funding": [
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"zustand": "^5.0.0"
|
"zustand": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.59.1",
|
||||||
"@testing-library/jest-dom": "^6.6.0",
|
"@testing-library/jest-dom": "^6.6.0",
|
||||||
"@testing-library/react": "^16.1.0",
|
"@testing-library/react": "^16.1.0",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user