Loading...
Loading...
This skill should be used when the user asks to "write electrobun tests", "add electrobun test coverage", "electrobun testing", "defineTest pattern", generating new electrobun test suites, or reverse-engineering electrobun component behaviour from test source.
npx skill4agent add dexploarer/plugin-electrobun-dev electrobun-testingkitchen/src/tests/defineTest()// kitchen/src/tests/window.test.ts
import { defineTest } from "../test-framework/defineTest";
export const windowTests = [
defineTest({
id: "window-creation-with-url", // stable slug — must be unique
title: "Window creation with URL",
category: "BrowserWindow",
description: "Verifies BrowserWindow can be created with a URL",
interactive: false, // automated: no human needed
apiSurface: ["BrowserWindow"], // APIs exercised (for manifest)
async run({ assert, log }) {
const win = new BrowserWindow({
title: "Test Window",
url: "views://test-harness/index.html",
hidden: true,
rpc: testRpc,
});
log("Window created, id:", win.id);
assert(win.id > 0, "Window should have valid id");
await win.close();
},
}),
];| Field | Type | Required | Description |
|---|---|---|---|
| string | ✅ | Stable slug (kebab-case, globally unique) |
| string | ✅ | Human-readable display name |
| string | ✅ | API grouping (e.g. "BrowserWindow", "Utils") |
| string | ✅ | One-sentence description |
| boolean | ✅ | |
| string[] | ✅ | Electrobun APIs exercised |
| async function | ✅ | Test body (see below) |
| string? | — | Playground HTML path if has UI |
| string[] | — | CSS selectors used in playground |
| string[] | — | e.g. |
async run({ assert, log, skip, fail }) {
// assert(condition, message) — fails test if false
assert(value === expected, "description of what was expected");
// log(message) — appears in test runner UI
log("Window id:", win.id);
// skip(reason) — marks test as skipped (not failed)
if (process.platform === "win32") skip("Not supported on Windows");
// fail(message) — immediately fail with message
fail("Should not reach here");
}// kitchen/src/tests/index.ts
import { windowTests } from "./window.test";
import { myNewTests } from "./my-new-feature.test";
export const allTests = [
...windowTests,
...myNewTests, // ← add here
];cd kitchen
npx tsx scripts/generate-manifest.tsinteractive: falsedefineTest({
id: "window-set-title",
title: "BrowserWindow.setTitle()",
category: "BrowserWindow",
description: "Verifies setTitle changes the window title",
interactive: false,
apiSurface: ["BrowserWindow"],
async run({ assert, log }) {
const win = new BrowserWindow({
title: "Original Title",
url: "views://test-harness/index.html",
hidden: true,
rpc: testRpc,
});
win.setTitle("New Title");
// Observe via evaluateJavascript or just test the API doesn't throw
log("Title changed successfully");
assert(true, "setTitle completed without error");
await win.close();
},
}),defineTest({
id: "rpc-request-response",
title: "RPC request-response round trip",
category: "RPC",
description: "Verifies bun-side request handler is callable from webview",
interactive: false,
apiSurface: ["BrowserView"],
async run({ assert, log }) {
const win = new BrowserWindow({
title: "RPC Test",
url: "views://test-harness/index.html",
hidden: true,
rpc: testRpc,
});
// Use evaluateJavascriptWithResponse to trigger webview-side RPC call
const result = await win.webview.rpc.request.evaluateJavascriptWithResponse({
script: `electrobun.rpc.request.someBunFunction({ a: 2, b: 3 })`,
});
assert(result === 5, `Expected 5, got ${result}`);
log("RPC round trip passed");
await win.close();
},
}),defineTest({
id: "utils-clipboard-text",
title: "Clipboard read/write text",
category: "Utils",
description: "Verifies clipboardWriteText and clipboardReadText round-trip",
interactive: false,
apiSurface: ["Utils"],
async run({ assert, log }) {
Utils.clipboardWriteText("electrobun-test-value");
const text = Utils.clipboardReadText();
assert(text === "electrobun-test-value", `Expected clipboard text, got: ${text}`);
Utils.clipboardClear();
log("Clipboard round-trip passed");
},
}),defineTest({
id: "navigation-block-rule",
title: "setNavigationRules blocks disallowed URL",
category: "Navigation",
description: "Verifies last-match-wins navigation rules",
interactive: false,
apiSurface: ["BrowserView"],
async run({ assert, log }) {
const win = new BrowserWindow({ url: "views://test-harness/index.html", hidden: true, rpc: testRpc });
win.webview.setNavigationRules([
{ match: "*", allow: false }, // block all
{ match: "views://*", allow: true }, // allow views:// (last match wins)
]);
// Navigate to allowed URL — should succeed
await win.webview.loadURL("views://test-harness/index.html");
log("Navigation rules applied");
await win.close();
},
}),interactive: truedefineTest({
id: "dialog-open-file",
title: "Open file dialog",
category: "Dialogs",
description: "Opens the native file picker and verifies a path is returned",
interactive: true,
apiSurface: ["Utils"],
playgroundRoute: "playgrounds/file-dialog/index.html",
uiSelectors: ["#openDialogBtn", "#result"],
async run({ assert, log, waitForReady, waitForVerify }) {
// Open the playground window
const playground = new BrowserWindow({
title: "File Dialog Playground",
url: "views://playgrounds/file-dialog/index.html",
rpc: playgroundRpc,
});
// Wait for user to click Start in the modal
await waitForReady();
// Instructions have been shown; user now interacts with playground window
// Wait for user to click Pass/Fail/Retest
const { action, notes } = await waitForVerify();
assert(action === "pass", `User marked ${action}: ${notes}`);
await playground.close();
},
}),new BrowserWindow(options)getById(id)webviewsetTitleclosefocusshowminimizeunminimizeisMinimizedmaximizeunmaximizeisMaximizedsetFullScreenisFullScreensetAlwaysOnTopisAlwaysOnTopsetVisibleOnAllWorkspacesisVisibleOnAllWorkspacessetPositionsetSizesetFramegetFramegetPositiongetSizesetPageZoomgetPageZoomon('close'|'move'|'resize'|'blur'|'focus')BrowserView.defineRPC(schema)new BrowserView(options)executeJavascriptloadURLloadHTMLsetNavigationRulesstopFindInPageopenDevToolscloseDevToolstoggleDevToolssetPageZoomgetPageZoomremovegetByIdgetAllon('will-navigate'|'did-navigate'|'dom-ready')rpc.request.evaluateJavascriptWithResponsenew Tray(options)setTitlesetImagesetMenusetVisiblegetBoundsremoveon('tray-clicked')getByIdgetAllremoveByIdApplicationMenu.setApplicationMenu(menu)ContextMenu.showContextMenu(menu)Electrobun.events.on('application-menu-clicked')Electrobun.events.on('context-menu-clicked')moveToTrashshowItemInFolderopenExternalopenPathsetDockIconVisibleisDockIconVisibleshowNotificationquitopenFileDialogshowMessageBoxclipboardReadTextclipboardWriteTextclipboardReadImageclipboardWriteImageclipboardClearclipboardAvailableFormatspaths.*Session.fromPartition(name)Session.defaultSessionsession.cookies.set/get/remove/clearScreen.getPrimaryDisplay()Screen.getAllDisplays()Screen.getCursorScreenPoint()GlobalShortcut.register(accelerator, handler)GlobalShortcut.unregister(accelerator)GlobalShortcut.unregisterAll()Updater.getLocalUpdater.onStatusChangeUpdater.checkForUpdateUpdater.downloadUpdateUpdater.updateInfoUpdater.applyUpdateUpdater.getStatusHistoryUpdater.clearStatusHistorycheckingupdate-availabledownloadingupdate-readyno-updateerrorElectroview.defineRPC(schema)new Electroview({ rpc })electrobun.rpc.request.*electrobun.rpc.send.*evaluateJavascriptWithResponse({ script })platformCaveatsplatformCaveats: ["macOS only"],
platformCaveats: ["requires CEF renderer"],
platformCaveats: ["unreliable on Linux window managers"],
platformCaveats: ["ARM Windows: commented out due to VM crashes"],cd kitchen
# After adding new defineTest() calls:
npx tsx scripts/generate-manifest.ts
# Verify everything is consistent:
npx tsx scripts/validate-manifest.ts
# Checks:
# 1. Every test in index.ts has manifest entries
# 2. Every playground route with a test is represented
# 3. All entries have required fields
# 4. No duplicate IDs