tauri-test-cli 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -102312,7 +102312,7 @@ var init_esm11 = __esm(() => {
102312
102312
  });
102313
102313
 
102314
102314
  // src/cli.ts
102315
- import { resolve } from "path";
102315
+ import { resolve as resolve2 } from "path";
102316
102316
 
102317
102317
  // src/driver.ts
102318
102318
  import { spawn, execSync } from "child_process";
@@ -120910,7 +120910,7 @@ var HTML2CANVAS_CDN = "https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2
120910
120910
  async function screenshot(options = {}) {
120911
120911
  const browser3 = requireBrowser();
120912
120912
  const autoWait = options.autoWait ?? true;
120913
- const timeout = options.timeout ?? 5000;
120913
+ const timeout = options.timeout ?? 15000;
120914
120914
  if (autoWait) {
120915
120915
  await waitForDomStable();
120916
120916
  }
@@ -121250,21 +121250,17 @@ async function waitFor(selector, options = {}) {
121250
121250
  // src/commands/eval.ts
121251
121251
  async function evaluate(script) {
121252
121252
  const browser3 = requireBrowser();
121253
- const wrappedScript = `
121254
- return (function() {
121255
- ${script}
121256
- })();
121257
- `;
121258
121253
  try {
121254
+ const result = await browser3.execute(`return ${script}`);
121255
+ return result;
121256
+ } catch {
121257
+ const wrappedScript = `
121258
+ return (function() {
121259
+ ${script}
121260
+ })();
121261
+ `;
121259
121262
  const result = await browser3.execute(wrappedScript);
121260
121263
  return result;
121261
- } catch (err) {
121262
- try {
121263
- const result = await browser3.execute(`return ${script}`);
121264
- return result;
121265
- } catch {
121266
- throw err;
121267
- }
121268
121264
  }
121269
121265
  }
121270
121266
 
@@ -121278,7 +121274,7 @@ var HTML2CANVAS_CDN2 = "https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html
121278
121274
  async function screenshot2(options = {}) {
121279
121275
  const browser3 = requireBrowser();
121280
121276
  const autoWait = options.autoWait ?? true;
121281
- const timeout = options.timeout ?? 5000;
121277
+ const timeout = options.timeout ?? 15000;
121282
121278
  if (autoWait) {
121283
121279
  await waitForDomStable();
121284
121280
  }
@@ -121618,21 +121614,17 @@ async function waitFor2(selector, options = {}) {
121618
121614
  // src/commands/eval.ts
121619
121615
  async function evaluate2(script) {
121620
121616
  const browser3 = requireBrowser();
121621
- const wrappedScript = `
121622
- return (function() {
121623
- ${script}
121624
- })();
121625
- `;
121626
121617
  try {
121618
+ const result = await browser3.execute(`return ${script}`);
121619
+ return result;
121620
+ } catch {
121621
+ const wrappedScript = `
121622
+ return (function() {
121623
+ ${script}
121624
+ })();
121625
+ `;
121627
121626
  const result = await browser3.execute(wrappedScript);
121628
121627
  return result;
121629
- } catch (err) {
121630
- try {
121631
- const result = await browser3.execute(`return ${script}`);
121632
- return result;
121633
- } catch {
121634
- throw err;
121635
- }
121636
121628
  }
121637
121629
  }
121638
121630
 
@@ -121662,7 +121654,8 @@ async function executeCommand(cmd, globalAutoWait) {
121662
121654
  result = await screenshot2({
121663
121655
  output: cmd.output,
121664
121656
  fullPage: cmd.fullPage,
121665
- autoWait
121657
+ autoWait,
121658
+ timeout: cmd.timeout
121666
121659
  });
121667
121660
  break;
121668
121661
  case "snapshot":
@@ -122176,6 +122169,246 @@ function stopXvfb() {
122176
122169
  }
122177
122170
  }
122178
122171
 
122172
+ // src/commands/install-skill.ts
122173
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
122174
+ import { resolve, dirname, join as join3 } from "path";
122175
+ import { homedir as homedir3 } from "os";
122176
+
122177
+ // src/generated/skill-content.ts
122178
+ var SKILL_MD_CONTENT = `---
122179
+ name: tauri-test-cli
122180
+ description: Use when needing to visually verify Tauri app behavior, test UI interactions, take screenshots, or inspect DOM state.
122181
+ ---
122182
+
122183
+ # tauri-test-cli
122184
+
122185
+ Visual testing CLI for Tauri apps. Start server once, send commands anytime.
122186
+
122187
+ ## Step 1: Detect Runtime & Set Command Prefix
122188
+
122189
+ > **Local dependency?** If \`node_modules/tauri-test-cli\` exists in the project, skip Steps 1-2 and use \`npx tauri-test-cli\` (or \`pnpm exec tauri-test-cli\` / \`bunx tauri-test-cli\`) as your command prefix.
122190
+
122191
+ Determine how to invoke tauri-test-cli based on the project:
122192
+
122193
+ \`\`\`bash
122194
+ # Check what's available
122195
+ ls pixi.toml bun.lock package-lock.json pnpm-lock.yaml 2>/dev/null
122196
+ \`\`\`
122197
+
122198
+ Set your command prefix for ALL subsequent commands:
122199
+ - **pixi + bun** (pixi.toml + bun.lock): \`pixi run bunx tauri-test-cli\`
122200
+ - **pixi + npm** (pixi.toml, no bun): \`pixi run npx tauri-test-cli\`
122201
+ - **bun** (bun.lock, no pixi): \`bunx tauri-test-cli\`
122202
+ - **npm** (package-lock.json): \`npx tauri-test-cli\`
122203
+ - **pnpm** (pnpm-lock.yaml): \`pnpm dlx tauri-test-cli\`
122204
+
122205
+ Use this prefix for EVERY command below. Examples below use \`CMD\` as placeholder.
122206
+
122207
+ ## Step 2: Ensure Latest Version
122208
+
122209
+ **bunx and npx cache aggressively.** Always clear all caches before running:
122210
+
122211
+ \`\`\`bash
122212
+ # Clear ALL known cache locations
122213
+ rm -rf /tmp/bunx-*tauri-test-cli* 2>/dev/null # bunx temp cache (primary)
122214
+ rm -rf ~/.bun/install/cache/tauri-test-cli* 2>/dev/null # bun install cache
122215
+ rm -rf ~/.npm/_npx/*/node_modules/tauri-test-cli 2>/dev/null # npx cache
122216
+ \`\`\`
122217
+
122218
+ This ensures you always get the latest published version.
122219
+
122220
+ ## Step 3: Check Dependencies
122221
+
122222
+ \`\`\`bash
122223
+ CMD check-deps
122224
+ \`\`\`
122225
+
122226
+ If tauri-driver is missing:
122227
+ \`\`\`bash
122228
+ CMD setup
122229
+ \`\`\`
122230
+
122231
+ ## Step 4: Find the App Binary
122232
+
122233
+ \`\`\`bash
122234
+ # Search for tauri.conf.json to find the app name
122235
+ find . -name "tauri.conf.json" -not -path "*/node_modules/*" 2>/dev/null
122236
+
122237
+ # Common binary locations:
122238
+ # ./target/debug/app-name
122239
+ # ./src-tauri/target/debug/app-name
122240
+ # ./apps/*/src-tauri/target/debug/app-name
122241
+ \`\`\`
122242
+
122243
+ Read \`tauri.conf.json\` to get the app identifier/name. Check if the binary exists:
122244
+ \`\`\`bash
122245
+ ls ./path/to/target/debug/app-name 2>/dev/null
122246
+ \`\`\`
122247
+
122248
+ If missing, build it:
122249
+ \`\`\`bash
122250
+ # If pixi project:
122251
+ pixi run cargo build --manifest-path ./path/to/src-tauri/Cargo.toml
122252
+ # Otherwise:
122253
+ cd ./path/to/src-tauri && cargo build
122254
+ \`\`\`
122255
+
122256
+ ## Step 5: Check if Dev Server Needed
122257
+
122258
+ Read \`tauri.conf.json\` — if it has a \`devUrl\` (e.g., \`http://localhost:5173\`), the debug binary needs the frontend dev server running.
122259
+
122260
+ \`\`\`bash
122261
+ # 1. Check if devUrl exists
122262
+ grep -o '"devUrl".*' path/to/tauri.conf.json
122263
+
122264
+ # 2. If devUrl found, check if the dev server is already reachable
122265
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:5173
122266
+
122267
+ # 3. If curl fails (connection refused / non-200), start the dev server
122268
+ # Look at package.json for the right dev command
122269
+ cd frontend-dir && npm run dev &
122270
+ sleep 5
122271
+
122272
+ # 4. Verify it's reachable now
122273
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:5173
122274
+ \`\`\`
122275
+
122276
+ If \`build/\` or \`dist/\` exists in the frontend directory, the binary may work without a dev server — but verify by checking the app loads correctly after starting the server.
122277
+
122278
+ ## Step 6: Clean Up & Start Server
122279
+
122280
+ **IMPORTANT on Linux:** Always use \`--xvfb\` flag. This avoids window focus and rendering issues.
122281
+
122282
+ \`\`\`bash
122283
+ # Clean up any stale processes first
122284
+ CMD cleanup
122285
+
122286
+ # Start server (--xvfb on Linux, omit on macOS)
122287
+ CMD server --app ./path/to/binary --xvfb &
122288
+
122289
+ # Wait for server — it can take 15-20 seconds
122290
+ sleep 15
122291
+ CMD status
122292
+ \`\`\`
122293
+
122294
+ If status says "No server running", wait longer:
122295
+ \`\`\`bash
122296
+ sleep 10
122297
+ CMD status
122298
+ \`\`\`
122299
+
122300
+ **DO NOT** try to manually start Xvfb, use the existing Wayland display, or work around \`--xvfb\` failures. If \`--xvfb\` fails, check the error message and report it.
122301
+
122302
+ ## Quick Reference
122303
+
122304
+ | Action | Command |
122305
+ |--------|---------|
122306
+ | Check deps | \`CMD check-deps\` |
122307
+ | Status | \`CMD status\` |
122308
+ | Start server | \`CMD server --app ./path/to/app --xvfb &\` |
122309
+ | Click | \`CMD click "selector"\` |
122310
+ | Type | \`CMD type "selector" "text"\` |
122311
+ | Screenshot | \`CMD screenshot --output /tmp/screen.png\` |
122312
+ | DOM snapshot | \`CMD snapshot --output /tmp/dom.yaml\` |
122313
+ | Wait appear | \`CMD wait "selector" --timeout 3000\` |
122314
+ | Wait gone | \`CMD wait "selector" --gone --timeout 5000\` |
122315
+ | Eval JS | \`CMD eval "document.title"\` |
122316
+ | Stop | \`CMD stop\` |
122317
+ | Cleanup | \`CMD cleanup\` |
122318
+
122319
+ ## After Server Starts
122320
+
122321
+ \`\`\`bash
122322
+ # Always take an initial screenshot to verify the app loaded
122323
+ CMD screenshot --output /tmp/initial.png
122324
+ # Use Read tool to view the screenshot!
122325
+
122326
+ # Run test commands
122327
+ CMD click "button"
122328
+ CMD screenshot --output /tmp/after-click.png
122329
+
122330
+ # When done
122331
+ CMD stop
122332
+ \`\`\`
122333
+
122334
+ ## Troubleshooting
122335
+
122336
+ | Error | Cause | Fix |
122337
+ |-------|-------|-----|
122338
+ | "Connection refused" in screenshot | Frontend dev server not running | Start frontend: \`npm run dev\` |
122339
+ | Blank/white screen | App not loaded yet | Increase \`--wait\` timeout |
122340
+ | "tauri-driver not found" | Missing dependency | Run \`CMD setup\` |
122341
+ | "Maximum sessions" error | Stale processes | Run \`CMD cleanup\` |
122342
+ | "Xvfb display failed to start" | Old CLI version (< 0.6.2) | Clear caches: \`rm -rf /tmp/bunx-*tauri-test-cli* ~/.bun/install/cache/tauri-test-cli*\` and retry |
122343
+ | Server not ready after 15s | Slow app startup | Wait longer, check if dev server is needed |
122344
+
122345
+ ## Screenshot vs Snapshot
122346
+
122347
+ - **Screenshot**: Visual appearance, layout, colors
122348
+ - **DOM snapshot**: Element existence, text content, attributes
122349
+ - **Use snapshot** for canvas elements (screenshots can't capture canvas)
122350
+
122351
+ ## Cleanup (if stuck)
122352
+
122353
+ \`\`\`bash
122354
+ CMD stop
122355
+ CMD cleanup
122356
+ pkill Xvfb 2>/dev/null # Linux only
122357
+ \`\`\`
122358
+ `;
122359
+
122360
+ // src/commands/install-skill.ts
122361
+ function findProjectRoot(cwd) {
122362
+ let dir = resolve(cwd);
122363
+ const root2 = dirname(dir) === dir ? dir : undefined;
122364
+ while (true) {
122365
+ if (existsSync(join3(dir, ".claude"))) {
122366
+ return dir;
122367
+ }
122368
+ const parent2 = dirname(dir);
122369
+ if (parent2 === dir)
122370
+ break;
122371
+ dir = parent2;
122372
+ }
122373
+ return null;
122374
+ }
122375
+ function installSkill(opts = {}) {
122376
+ let targetDir;
122377
+ let mode;
122378
+ if (opts.global && opts.project) {
122379
+ console.error("Error: cannot specify both --global and --project");
122380
+ return { success: false };
122381
+ }
122382
+ if (opts.global) {
122383
+ targetDir = join3(homedir3(), ".claude", "skills", "tauri-test-cli");
122384
+ mode = "global";
122385
+ } else if (opts.project) {
122386
+ targetDir = join3(process.cwd(), ".claude", "skills", "tauri-test-cli");
122387
+ mode = "project";
122388
+ } else {
122389
+ const projectRoot = findProjectRoot(process.cwd());
122390
+ if (projectRoot) {
122391
+ targetDir = join3(projectRoot, ".claude", "skills", "tauri-test-cli");
122392
+ mode = `project (${projectRoot})`;
122393
+ } else {
122394
+ targetDir = join3(homedir3(), ".claude", "skills", "tauri-test-cli");
122395
+ mode = "global";
122396
+ }
122397
+ }
122398
+ const targetPath = join3(targetDir, "SKILL.md");
122399
+ if (existsSync(targetPath)) {
122400
+ const existing = readFileSync2(targetPath, "utf-8");
122401
+ if (existing === SKILL_MD_CONTENT) {
122402
+ console.log(`Skill already up-to-date (${mode}): ${targetPath}`);
122403
+ return { success: true };
122404
+ }
122405
+ }
122406
+ mkdirSync(targetDir, { recursive: true });
122407
+ writeFileSync(targetPath, SKILL_MD_CONTENT, "utf-8");
122408
+ console.log(`Installed skill (${mode}): ${targetPath}`);
122409
+ return { success: true };
122410
+ }
122411
+
122179
122412
  // src/cli.ts
122180
122413
  async function sendToServer(port, cmd) {
122181
122414
  const url2 = `http://127.0.0.1:${port}`;
@@ -122256,6 +122489,9 @@ COMMANDS:
122256
122489
  check-deps
122257
122490
  Check if all required system dependencies are installed
122258
122491
 
122492
+ install-skill [--global | --project]
122493
+ Install Claude Code skill for tauri-test-cli
122494
+
122259
122495
  help
122260
122496
  Show this help message
122261
122497
 
@@ -122375,7 +122611,8 @@ async function executeCommand2(cmd, globalAutoWait) {
122375
122611
  return await screenshot({
122376
122612
  output: cmd.output,
122377
122613
  fullPage: cmd.fullPage,
122378
- autoWait
122614
+ autoWait,
122615
+ timeout: cmd.timeout
122379
122616
  });
122380
122617
  case "snapshot":
122381
122618
  return await snapshot({
@@ -122412,7 +122649,7 @@ async function executeCommand2(cmd, globalAutoWait) {
122412
122649
  }
122413
122650
  return await evaluate(cmd.script);
122414
122651
  case "sleep":
122415
- await new Promise((resolve2) => setTimeout(resolve2, cmd.ms ?? 1000));
122652
+ await new Promise((resolve3) => setTimeout(resolve3, cmd.ms ?? 1000));
122416
122653
  return { slept: cmd.ms ?? 1000 };
122417
122654
  default:
122418
122655
  throw new Error(`Unknown command: ${cmd.cmd}`);
@@ -122484,7 +122721,14 @@ async function main() {
122484
122721
  process.exit(1);
122485
122722
  }
122486
122723
  }
122487
- const appPath = options.app ? resolve(options.app) : undefined;
122724
+ if (command === "install-skill") {
122725
+ const result = installSkill({
122726
+ global: !!options.global,
122727
+ project: !!options.project
122728
+ });
122729
+ process.exit(result.success ? 0 : 1);
122730
+ }
122731
+ const appPath = options.app ? resolve2(options.app) : undefined;
122488
122732
  const jsonOutput = !!options.json;
122489
122733
  const port = options.port ? parseInt(options.port) : 9222;
122490
122734
  const clientModeCommands = ["screenshot", "snapshot", "click", "type", "wait", "eval"];
@@ -122507,7 +122751,8 @@ async function main() {
122507
122751
  cmd = {
122508
122752
  cmd: "screenshot",
122509
122753
  output: options.output,
122510
- fullPage: !!options["full-page"]
122754
+ fullPage: !!options["full-page"],
122755
+ timeout: options.timeout ? parseInt(options.timeout) : undefined
122511
122756
  };
122512
122757
  break;
122513
122758
  case "snapshot":
@@ -122666,7 +122911,8 @@ async function main() {
122666
122911
  result = await screenshot({
122667
122912
  output: options.output,
122668
122913
  fullPage: !!options["full-page"],
122669
- autoWait
122914
+ autoWait,
122915
+ timeout: options.timeout ? parseInt(options.timeout) : undefined
122670
122916
  });
122671
122917
  break;
122672
122918
  case "snapshot":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tauri-test-cli",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for testing Tauri applications with screenshot capture, DOM inspection, and user interaction simulation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "typecheck": "tsc --noEmit",
31
31
  "test": "bun test",
32
32
  "test:watch": "bun test --watch",
33
- "prepublishOnly": "bun run build && bun test",
33
+ "generate-skill-content": "bun scripts/generate-skill-content.ts",
34
+ "prepublishOnly": "bun run generate-skill-content && bun run build && bun test",
34
35
  "postinstall": "node scripts/postinstall.js"
35
36
  },
36
37
  "dependencies": {
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Reads skills/tauri-test-cli/SKILL.md and generates src/generated/skill-content.ts
4
+ * with the content embedded as a string constant for bundling.
5
+ */
6
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
7
+ import { resolve, dirname } from "path";
8
+
9
+ const ROOT = resolve(dirname(new URL(import.meta.url).pathname), "..");
10
+ const SKILL_PATH = resolve(ROOT, "skills/tauri-test-cli/SKILL.md");
11
+ const OUT_DIR = resolve(ROOT, "src/generated");
12
+ const OUT_PATH = resolve(OUT_DIR, "skill-content.ts");
13
+
14
+ const raw = readFileSync(SKILL_PATH, "utf-8");
15
+
16
+ // Escape for template literal embedding
17
+ const escaped = raw
18
+ .replace(/\\/g, "\\\\")
19
+ .replace(/`/g, "\\`")
20
+ .replace(/\$/g, "\\$");
21
+
22
+ const output = `// AUTO-GENERATED — do not edit. Run: bun scripts/generate-skill-content.ts
23
+ export const SKILL_MD_CONTENT = \`${escaped}\`;
24
+ `;
25
+
26
+ mkdirSync(OUT_DIR, { recursive: true });
27
+ writeFileSync(OUT_PATH, output, "utf-8");
28
+ console.log(`Generated ${OUT_PATH}`);