testdriverai 7.1.4 → 7.2.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.
Files changed (70) hide show
  1. package/.github/workflows/acceptance.yaml +81 -0
  2. package/.github/workflows/publish.yaml +44 -0
  3. package/agent/index.js +18 -19
  4. package/agent/lib/commands.js +321 -121
  5. package/agent/lib/redraw.js +99 -39
  6. package/agent/lib/sandbox.js +98 -6
  7. package/agent/lib/sdk.js +25 -0
  8. package/agent/lib/system.js +2 -1
  9. package/agent/lib/validation.js +6 -6
  10. package/docs/docs.json +211 -101
  11. package/docs/snippets/tests/type-repeated-replay.mdx +1 -1
  12. package/docs/v7/_drafts/caching-selectors.mdx +24 -0
  13. package/docs/v7/api/act.mdx +1 -1
  14. package/docs/v7/api/assert.mdx +1 -1
  15. package/docs/v7/api/assertions.mdx +7 -7
  16. package/docs/v7/api/elements.mdx +78 -0
  17. package/docs/v7/api/find.mdx +38 -0
  18. package/docs/v7/api/focusApplication.mdx +2 -2
  19. package/docs/v7/api/hover.mdx +2 -2
  20. package/docs/v7/features/ai-native.mdx +57 -71
  21. package/docs/v7/features/application-logs.mdx +353 -0
  22. package/docs/v7/features/browser-logs.mdx +414 -0
  23. package/docs/v7/features/cache-management.mdx +402 -0
  24. package/docs/v7/features/continuous-testing.mdx +346 -0
  25. package/docs/v7/features/coverage.mdx +508 -0
  26. package/docs/v7/features/data-driven-testing.mdx +441 -0
  27. package/docs/v7/features/easy-to-write.mdx +2 -73
  28. package/docs/v7/features/enterprise.mdx +155 -39
  29. package/docs/v7/features/fast.mdx +63 -81
  30. package/docs/v7/features/managed-sandboxes.mdx +384 -0
  31. package/docs/v7/features/network-monitoring.mdx +568 -0
  32. package/docs/v7/features/observable.mdx +3 -22
  33. package/docs/v7/features/parallel-execution.mdx +381 -0
  34. package/docs/v7/features/powerful.mdx +1 -1
  35. package/docs/v7/features/reports.mdx +414 -0
  36. package/docs/v7/features/sandbox-customization.mdx +229 -0
  37. package/docs/v7/features/scalable.mdx +217 -2
  38. package/docs/v7/features/stable.mdx +106 -147
  39. package/docs/v7/features/system-performance.mdx +616 -0
  40. package/docs/v7/features/test-analytics.mdx +373 -0
  41. package/docs/v7/features/test-cases.mdx +393 -0
  42. package/docs/v7/features/test-replays.mdx +408 -0
  43. package/docs/v7/features/test-reports.mdx +308 -0
  44. package/docs/v7/getting-started/{running-and-debugging.mdx → debugging-tests.mdx} +12 -142
  45. package/docs/v7/getting-started/quickstart.mdx +22 -305
  46. package/docs/v7/getting-started/running-tests.mdx +173 -0
  47. package/docs/v7/overview/what-is-testdriver.mdx +2 -14
  48. package/docs/v7/presets/chrome-extension.mdx +147 -122
  49. package/interfaces/cli/commands/init.js +3 -3
  50. package/interfaces/cli/lib/base.js +3 -2
  51. package/interfaces/logger.js +0 -2
  52. package/interfaces/shared-test-state.mjs +0 -5
  53. package/interfaces/vitest-plugin.mjs +69 -42
  54. package/lib/core/Dashcam.js +65 -66
  55. package/lib/vitest/hooks.mjs +42 -50
  56. package/package.json +1 -1
  57. package/sdk-log-formatter.js +350 -175
  58. package/sdk.js +431 -116
  59. package/setup/aws/cloudformation.yaml +2 -2
  60. package/setup/aws/self-hosted.yml +1 -1
  61. package/test/testdriver/chrome-extension.test.mjs +55 -72
  62. package/test/testdriver/element-not-found.test.mjs +2 -1
  63. package/test/testdriver/hover-image.test.mjs +1 -1
  64. package/test/testdriver/scroll-until-text.test.mjs +10 -6
  65. package/test/testdriver/setup/lifecycleHelpers.mjs +19 -24
  66. package/test/testdriver/setup/testHelpers.mjs +18 -23
  67. package/vitest.config.mjs +3 -3
  68. package/.github/workflows/linux-tests.yml +0 -28
  69. package/docs/v7/getting-started/generating-tests.mdx +0 -525
  70. package/test/testdriver/auto-cache-key-demo.test.mjs +0 -56
@@ -0,0 +1,81 @@
1
+ name: Acceptance Tests
2
+ permissions:
3
+ id-token: write
4
+ contents: write
5
+ pull-requests: write
6
+ checks: write
7
+ on:
8
+ pull_request:
9
+ branches: [ main ]
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ test-linux:
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Setup Node.js
23
+ uses: actions/setup-node@v4
24
+ with:
25
+ node-version: '20'
26
+ cache: 'npm'
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Run Linux tests
32
+ run: npx vitest run test/testdriver/*.test.mjs
33
+ env:
34
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
35
+ TD_OS: linux
36
+
37
+ - name: Upload test results to Sentry Prevent
38
+ if: ${{ !cancelled() }}
39
+ uses: getsentry/prevent-action@v0
40
+
41
+ - name: Publish Test Results
42
+ uses: EnricoMi/publish-unit-test-result-action@v2
43
+ if: always()
44
+ with:
45
+ files: test-report.junit.xml
46
+ comment_mode: always
47
+ check_name: Test Results (Linux)
48
+
49
+ test-windows:
50
+ runs-on: ubuntu-latest
51
+ if: contains(github.event.pull_request.labels.*.name, 'test-windows')
52
+
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - name: Setup Node.js
57
+ uses: actions/setup-node@v4
58
+ with:
59
+ node-version: '20'
60
+ cache: 'npm'
61
+
62
+ - name: Install dependencies
63
+ run: npm ci
64
+
65
+ - name: Run Windows tests
66
+ run: npx vitest run test/testdriver/*.test.mjs
67
+ env:
68
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
69
+ TD_OS: windows
70
+
71
+ - name: Upload test results to Sentry Prevent
72
+ if: ${{ !cancelled() }}
73
+ uses: getsentry/prevent-action@v0
74
+
75
+ - name: Publish Test Results
76
+ uses: EnricoMi/publish-unit-test-result-action@v2
77
+ if: always()
78
+ with:
79
+ files: test-report.junit.xml
80
+ comment_mode: always
81
+ check_name: Test Results (Windows)
@@ -0,0 +1,44 @@
1
+ name: Publish Beta
2
+ permissions:
3
+ contents: write
4
+ on:
5
+ push:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ publish-beta:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0
16
+ token: ${{ secrets.GITHUB_TOKEN }}
17
+
18
+ - name: Setup Node.js
19
+ uses: actions/setup-node@v4
20
+ with:
21
+ node-version: '20'
22
+ registry-url: 'https://registry.npmjs.org/'
23
+
24
+ - name: Configure Git
25
+ run: |
26
+ git config user.name "github-actions[bot]"
27
+ git config user.email "github-actions[bot]@users.noreply.github.com"
28
+
29
+ - name: Install dependencies
30
+ run: npm ci
31
+
32
+ - name: Bump version (prerelease beta)
33
+ run: npm version prerelease --preid=beta --no-git-tag-version
34
+
35
+ - name: Commit and push version bump
36
+ run: |
37
+ git add package.json package-lock.json
38
+ git commit -m "chore: bump beta version to $(node -p "require('./package.json').version")"
39
+ git push
40
+
41
+ - name: Publish to npm under beta tag
42
+ run: npm publish --tag beta
43
+ env:
44
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/agent/index.js CHANGED
@@ -183,7 +183,8 @@ class TestDriverAgent extends EventEmitter2 {
183
183
  // single function to handle all program exits
184
184
  // allows us to save the current state, run lifecycle hooks, and track analytics
185
185
  async exit(failed = true, shouldSave = false, shouldRunPostrun = false) {
186
- this.emitter.emit(events.log.narration, theme.dim("exiting..."), true);
186
+ const { formatter } = require("../sdk-log-formatter.js");
187
+ this.emitter.emit(events.log.narration, formatter.getPrefix("disconnect") + " " + theme.yellow.bold("Exiting") + theme.dim("..."), true);
187
188
 
188
189
  // Clean up redraw interval
189
190
  if (this.redraw && this.redraw.cleanup) {
@@ -1838,6 +1839,9 @@ ${regression}
1838
1839
  }
1839
1840
  }
1840
1841
 
1842
+ // Create session first so session ID is available for Sentry tracing in WebSocket connection
1843
+ await this.newSession();
1844
+
1841
1845
  // order is important!
1842
1846
  await this.connectToSandboxService();
1843
1847
 
@@ -1856,7 +1860,6 @@ ${regression}
1856
1860
 
1857
1861
  this.instance = instance.instance;
1858
1862
  await this.renderSandbox(this.instance, headless);
1859
- await this.newSession();
1860
1863
  await this.runLifecycle("provision");
1861
1864
 
1862
1865
  return;
@@ -1877,7 +1880,6 @@ ${regression}
1877
1880
  this.instance = instance;
1878
1881
 
1879
1882
  await this.renderSandbox(instance, headless);
1880
- await this.newSession();
1881
1883
  return;
1882
1884
  } catch (error) {
1883
1885
  // If connection fails, fall through to creating a new sandbox
@@ -1909,7 +1911,6 @@ ${regression}
1909
1911
  this.instance = instance;
1910
1912
 
1911
1913
  await this.renderSandbox(instance, headless);
1912
- await this.newSession();
1913
1914
  return;
1914
1915
  } catch (error) {
1915
1916
  // If connection fails, fall through to creating a new sandbox
@@ -1923,9 +1924,10 @@ ${regression}
1923
1924
 
1924
1925
  // Create new sandbox (either because createNew is true, or no existing sandbox to connect to)
1925
1926
  if (!this.instance) {
1927
+ const { formatter } = require("../sdk-log-formatter.js");
1926
1928
  this.emitter.emit(
1927
1929
  events.log.narration,
1928
- theme.dim(`creating new sandbox...`),
1930
+ formatter.getPrefix("connect") + " " + theme.green.bold("Creating") + " " + theme.cyan(`new sandbox...`),
1929
1931
  );
1930
1932
  // We don't have resiliency/retries baked in, so let's at least give it 1 attempt
1931
1933
  // to see if that fixes the issue.
@@ -1949,10 +1951,7 @@ ${regression}
1949
1951
  );
1950
1952
  this.instance = instance;
1951
1953
  await this.renderSandbox(instance, headless);
1952
- await this.newSession();
1953
1954
  await this.runLifecycle("provision");
1954
-
1955
- console.log("provision run");
1956
1955
  }
1957
1956
  }
1958
1957
 
@@ -2073,7 +2072,6 @@ ${regression}
2073
2072
  }
2074
2073
 
2075
2074
  async renderSandbox(instance, headless = false) {
2076
- console.log("renderSandbox", instance);
2077
2075
 
2078
2076
  if (!headless) {
2079
2077
  let url;
@@ -2126,7 +2124,8 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2126
2124
  );
2127
2125
  }
2128
2126
 
2129
- this.emitter.emit(events.log.narration, theme.dim(`authenticating...`));
2127
+ const { formatter } = require("../sdk-log-formatter.js");
2128
+ this.emitter.emit(events.log.narration, formatter.getPrefix("connect") + " " + theme.green.bold("Authenticating") + theme.dim("..."));
2130
2129
  let ableToAuth = await this.sandbox.auth(this.config.TD_API_KEY);
2131
2130
 
2132
2131
  if (!ableToAuth) {
@@ -2139,7 +2138,8 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2139
2138
  }
2140
2139
 
2141
2140
  async connectToSandboxDirect(sandboxId, persist = false) {
2142
- this.emitter.emit(events.log.narration, theme.dim(`connecting...`));
2141
+ const { formatter } = require("../sdk-log-formatter.js");
2142
+ this.emitter.emit(events.log.narration, formatter.getPrefix("connect") + " " + theme.green.bold("Connecting") + " " + theme.cyan(`to sandbox...`));
2143
2143
  let reply = await this.sandbox.connect(sandboxId, persist);
2144
2144
 
2145
2145
  // reply includes { success, url, sandbox: {...} }
@@ -2170,7 +2170,7 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2170
2170
  sandboxConfig.instanceType = this.sandboxInstance;
2171
2171
  }
2172
2172
 
2173
- let instance = await this.sandbox.send(sandboxConfig);
2173
+ let instance = await this.sandbox.send(sandboxConfig, 60000 * 8);
2174
2174
 
2175
2175
  // Save the sandbox ID for reconnection with the correct OS type
2176
2176
  if (instance.sandbox && instance.sandbox.sandboxId) {
@@ -2184,10 +2184,13 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2184
2184
 
2185
2185
  async newSession() {
2186
2186
  // should be start of new session
2187
+ // If sandbox is connected, get system info; otherwise pass empty objects
2188
+ const isSandboxConnected = this.sandbox.apiSocketConnected;
2189
+
2187
2190
  const sessionRes = await this.sdk.req("session/start", {
2188
- systemInformationOsInfo: await this.system.getSystemInformationOsInfo(),
2189
- mousePosition: await this.system.getMousePosition(),
2190
- activeWindow: await this.system.activeWin(),
2191
+ systemInformationOsInfo: isSandboxConnected ? await this.system.getSystemInformationOsInfo() : {},
2192
+ mousePosition: isSandboxConnected ? await this.system.getMousePosition() : {},
2193
+ activeWindow: isSandboxConnected ? await this.system.activeWin() : {},
2191
2194
  });
2192
2195
 
2193
2196
  if (!sessionRes) {
@@ -2265,9 +2268,6 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2265
2268
  // If sourceMapper doesn't have a current file, use thisFile which should be the file being run
2266
2269
  let currentFilePath = this.sourceMapper.currentFilePath || this.thisFile;
2267
2270
 
2268
- this.emitter.emit(events.log.log, ``);
2269
- this.emitter.emit(events.log.log, "Running lifecycle: " + lifecycleName);
2270
-
2271
2271
  // If we still don't have a currentFilePath, fall back to the default testdriver directory
2272
2272
  if (!currentFilePath) {
2273
2273
  currentFilePath = path.join(
@@ -2275,7 +2275,6 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2275
2275
  "testdriver",
2276
2276
  "testdriver.yaml",
2277
2277
  );
2278
- console.log("No currentFilePath found, using fallback:", currentFilePath);
2279
2278
  }
2280
2279
 
2281
2280
  // Ensure we have an absolute path