testdriverai 7.11.225-test → 7.11.227-test

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/docs/docs.json CHANGED
@@ -179,7 +179,7 @@
179
179
  "redirects": [
180
180
  {
181
181
  "source": "/",
182
- "destination": "/quickstart-github"
182
+ "destination": "/quickstart"
183
183
  },
184
184
  {
185
185
  "source": "/quickstart-ai-setup",
@@ -142,7 +142,7 @@ await testdriver.provision.installer(options)
142
142
  </ParamField>
143
143
 
144
144
  <ParamField path="launch" type="boolean" default={true}>
145
- Whether to focus/launch the app after installation.
145
+ Whether to launch the app after installation. For a `.deb` on Linux, the app is started from its `.desktop` entry (or, when `appName` is set and there is no `.desktop` entry, from its `/usr/bin` binary) and then `appName` is focused. On Windows and macOS, `appName` is focused.
146
146
  </ParamField>
147
147
  </Expandable>
148
148
  </ParamField>
@@ -151,7 +151,9 @@ await testdriver.provision.installer(options)
151
151
  - Downloads the installer from the URL
152
152
  - Auto-detects the install method based on file extension
153
153
  - Runs the appropriate install command (e.g., `msiexec` for `.msi`, `dpkg` for `.deb`)
154
- - Optionally focuses the installed application
154
+ - For `.deb` packages, installs any missing dependencies with `apt-get install -f` and fails if the package isn't fully installed
155
+ - For `.deb` packages on Linux with `launch: true`, starts the app in the background
156
+ - Focuses `appName` when it's set and `launch` is `true`
155
157
 
156
158
  ```javascript
157
159
  // Windows MSI installer
@@ -160,11 +162,10 @@ await testdriver.provision.installer({
160
162
  appName: 'MyApp',
161
163
  });
162
164
 
163
- // Linux DEB package
165
+ // Linux DEB package: installs dependencies, starts the app, focuses it
164
166
  await testdriver.provision.installer({
165
167
  url: 'https://example.com/app.deb',
166
168
  appName: 'MyApp',
167
- launch: true,
168
169
  });
169
170
  ```
170
171
 
@@ -28,6 +28,32 @@ describe("Provision Installer", () => {
28
28
  },
29
29
  );
30
30
 
31
+ it.skipIf(!isLinux)(
32
+ "should install a .deb with missing dependencies and launch its window",
33
+ async (context) => {
34
+ const testdriver = TestDriver(context, { ...getDefaults(context) });
35
+
36
+ // Yaak is a Tauri app. Its .deb depends on WebKitGTK and libsoup,
37
+ // which aren't preinstalled, so dpkg alone leaves it unconfigured.
38
+ await testdriver.provision.installer({
39
+ url: 'https://github.com/mountain-loop/yaak/releases/download/v2026.9.0/yaak_2026.9.0_amd64.deb',
40
+ appName: 'Yaak',
41
+ });
42
+
43
+ // The package and its dependencies are fully installed
44
+ const status = await testdriver.exec(
45
+ 'sh',
46
+ "dpkg-query -W -f='${Status}' yaak",
47
+ 10000,
48
+ );
49
+ expect(status).toBe('install ok installed');
50
+
51
+ // launch: true (the default) started the app, so its window is on screen
52
+ const result = await testdriver.assert('The Yaak app window is open');
53
+ expect(result).toBeTruthy();
54
+ },
55
+ );
56
+
31
57
  it.skipIf(!isLinux)(
32
58
  "should download a shell script and verify it exists",
33
59
  async (context) => {
package/lib/provision.js CHANGED
@@ -479,10 +479,14 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
479
479
  * @param {string} options.url - URL to download the installer from
480
480
  * @param {string} [options.filename] - Filename to save as (auto-detected from URL if not provided)
481
481
  * @param {string} [options.appName] - Application name to focus after install
482
- * @param {boolean} [options.launch=true] - Whether to launch the app after installation
482
+ * @param {boolean} [options.launch=true] - Whether to launch the app after installation.
483
+ * For a .deb on Linux this starts the app from its `.desktop` entry (or, when
484
+ * `appName` is set and there is no `.desktop` entry, its first `/usr/bin` binary),
485
+ * then focuses `appName`. On other platforms it only focuses `appName`.
483
486
  * @returns {Promise<string>} Path to the downloaded file
484
487
  * @example
485
- * // Install a .deb package on Linux (auto-detected)
488
+ * // Install a .deb package on Linux (auto-detected). Missing dependencies
489
+ * // are installed with apt, then the app is started and focused.
486
490
  * await testdriver.provision.installer({
487
491
  * url: 'https://example.com/app.deb',
488
492
  * appName: 'MyApp'
@@ -592,7 +596,25 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
592
596
  }
593
597
  } else if (self.os === "linux") {
594
598
  if (ext === "deb") {
595
- installCommand = `sudo dpkg -i "${actualFilePath}" && sudo apt-get install -f -y`;
599
+ // dpkg -i exits non-zero when dependencies are missing, so resolve
600
+ // them with apt-get install -f instead of stopping there. Fail the
601
+ // step if the package still isn't fully installed afterwards.
602
+ // Runs in a subshell: the sandbox shell is persistent, so a bare
603
+ // `exit` would kill it instead of failing the command.
604
+ installCommand = [
605
+ `(`,
606
+ `PKG=$(dpkg-deb -f "${actualFilePath}" Package)`,
607
+ `if ! sudo DEBIAN_FRONTEND=noninteractive dpkg -i "${actualFilePath}"; then`,
608
+ ` sudo apt-get update -qq`,
609
+ ` sudo DEBIAN_FRONTEND=noninteractive apt-get install -f -y`,
610
+ `fi`,
611
+ `if ! dpkg -s "$PKG" 2>/dev/null | grep -q '^Status: install ok installed'; then`,
612
+ ` echo "[provision.installer] package $PKG is not fully installed" >&2`,
613
+ ` dpkg -l "$PKG" >&2`,
614
+ ` exit 1`,
615
+ `fi`,
616
+ `)`,
617
+ ].join("\n");
596
618
  } else if (ext === "rpm") {
597
619
  installCommand = `sudo rpm -i "${actualFilePath}"`;
598
620
  } else if (ext === "appimage") {
@@ -614,8 +636,47 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
614
636
  console.log(`[provision.installer] ✅ Installation complete`);
615
637
  }
616
638
 
617
- // Launch and focus the app if appName is provided and launch is true
639
+ // On Linux, installing a .deb doesn't start anything. Start the app
640
+ // from its .desktop entry (or a /usr/bin binary when appName is set)
641
+ // so a window exists to focus and test.
642
+ let launched = false;
643
+ if (launch && self.os === "linux" && ext === "deb") {
644
+ const launchScript = [
645
+ `(`,
646
+ `PKG=$(dpkg-deb -f "${actualFilePath}" Package)`,
647
+ `FILES=$(dpkg -L "$PKG")`,
648
+ `DESKTOP=$(echo "$FILES" | grep -E '^/usr/(local/)?share/applications/.*\\.desktop$' | head -n1)`,
649
+ `CMD=""`,
650
+ `if [ -n "$DESKTOP" ]; then`,
651
+ ` CMD=$(grep -m1 '^Exec=' "$DESKTOP" | sed -e 's/^Exec=//' -e 's/ *%[a-zA-Z]//g')`,
652
+ `fi`,
653
+ appName
654
+ ? `if [ -z "$CMD" ]; then CMD=$(echo "$FILES" | grep -E '^/usr/(local/)?bin/[^/]+$' | head -n1); fi`
655
+ : ``,
656
+ `if [ -n "$CMD" ]; then`,
657
+ ` nohup sh -c "$CMD" >/dev/null 2>&1 &`,
658
+ ` echo "TD_LAUNCHED=$CMD"`,
659
+ `fi`,
660
+ `)`,
661
+ ].join("\n");
662
+ const output = await self.exec(shell, launchScript, 30000, true);
663
+ const match = /TD_LAUNCHED=(.+)/.exec(output || "");
664
+ if (match) {
665
+ launched = true;
666
+ console.log(`[provision.installer] ✅ Launched ${match[1].trim()}`);
667
+ } else {
668
+ console.log(
669
+ `[provision.installer] No .desktop entry found, skipping launch`,
670
+ );
671
+ }
672
+ }
673
+
674
+ // Focus the app if appName is provided and launch is true
618
675
  if (appName && launch) {
676
+ if (launched) {
677
+ // Give the app time to open its first window
678
+ await new Promise((resolve) => setTimeout(resolve, 5000));
679
+ }
619
680
  await new Promise((resolve) => setTimeout(resolve, 2000));
620
681
  await self.focusApplication(appName);
621
682
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.11.225-test",
3
+ "version": "7.11.227-test",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "types": "sdk.d.ts",
package/sdk.d.ts CHANGED
@@ -903,7 +903,7 @@ export interface ProvisionInstallerOptions {
903
903
  filename?: string;
904
904
  /** Application name to focus after install */
905
905
  appName?: string;
906
- /** Whether to launch the app after installation (default: true) */
906
+ /** Whether to launch the app after installation (default: true). For a .deb on Linux this starts the app from its .desktop entry, then focuses appName. */
907
907
  launch?: boolean;
908
908
  }
909
909