apify-test-tools 0.10.2-beta.2 → 0.10.2-beta.20

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 (275) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/actionlint.yaml +11 -0
  3. package/.github/actions/checkout-restore-dependencies/action.yaml +58 -6
  4. package/.github/cli/apify-test-tools.mjs +135 -142
  5. package/.github/scripts/mask-config-secrets.sh +17 -0
  6. package/.github/scripts/run-with-apify-tokens.mjs +63 -103
  7. package/.github/workflows/_check_code.yaml +5 -21
  8. package/.github/workflows/_move_major_tag.yaml +2 -2
  9. package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +89 -8
  10. package/.github/workflows/public_platform-tests.yaml +68 -30
  11. package/.github/workflows/public_pr-build-test.yaml +77 -38
  12. package/.github/workflows/public_push-build-latest.yaml +49 -11
  13. package/.github/workflows/public_review.yaml +5 -4
  14. package/.github/workflows/public_stale-issues.yaml +51 -0
  15. package/.husky/pre-commit +4 -4
  16. package/.prettierignore +2 -1
  17. package/CHANGELOG.md +22 -0
  18. package/CONTRIBUTING.md +117 -17
  19. package/README.md +221 -595
  20. package/bin/actor-ids.ts +41 -0
  21. package/bin/affected-actors.ts +211 -0
  22. package/bin/build-from-local.ts +19 -1
  23. package/bin/build.ts +105 -39
  24. package/bin/diff-changes.ts +69 -36
  25. package/bin/diff-json-schema.ts +5 -6
  26. package/bin/git.ts +73 -175
  27. package/bin/main.ts +142 -189
  28. package/bin/notify.ts +198 -0
  29. package/bin/run-tests.ts +167 -0
  30. package/bin/slack.ts +16 -76
  31. package/bin/stdin-input.ts +156 -0
  32. package/bin/test-files-glob.ts +126 -0
  33. package/bin/test-report.ts +141 -140
  34. package/bin/types.ts +4 -7
  35. package/bin/utils/config/load-config.ts +20 -2
  36. package/bin/utils/config/parser.ts +40 -8
  37. package/bin/utils/config/structures/grouped.ts +2 -2
  38. package/bin/utils/config/structures/legacy.ts +1 -1
  39. package/dist/bin/actor-ids.d.ts +15 -0
  40. package/dist/bin/actor-ids.d.ts.map +1 -0
  41. package/dist/bin/actor-ids.js +30 -0
  42. package/dist/bin/actor-ids.js.map +1 -0
  43. package/dist/bin/affected-actors.d.ts +55 -0
  44. package/dist/bin/affected-actors.d.ts.map +1 -0
  45. package/dist/bin/affected-actors.js +142 -0
  46. package/dist/bin/affected-actors.js.map +1 -0
  47. package/dist/bin/build-from-local.d.ts +2 -1
  48. package/dist/bin/build-from-local.d.ts.map +1 -1
  49. package/dist/bin/build-from-local.js +6 -2
  50. package/dist/bin/build-from-local.js.map +1 -1
  51. package/dist/bin/build.d.ts +18 -5
  52. package/dist/bin/build.d.ts.map +1 -1
  53. package/dist/bin/build.js +74 -24
  54. package/dist/bin/build.js.map +1 -1
  55. package/dist/bin/diff-changes.d.ts +21 -5
  56. package/dist/bin/diff-changes.d.ts.map +1 -1
  57. package/dist/bin/diff-changes.js +41 -28
  58. package/dist/bin/diff-changes.js.map +1 -1
  59. package/dist/bin/diff-json-schema.d.ts +2 -2
  60. package/dist/bin/diff-json-schema.d.ts.map +1 -1
  61. package/dist/bin/diff-json-schema.js +1 -4
  62. package/dist/bin/diff-json-schema.js.map +1 -1
  63. package/dist/bin/git.d.ts +26 -34
  64. package/dist/bin/git.d.ts.map +1 -1
  65. package/dist/bin/git.js +60 -143
  66. package/dist/bin/git.js.map +1 -1
  67. package/dist/bin/main.js +126 -140
  68. package/dist/bin/main.js.map +1 -1
  69. package/dist/bin/notify.d.ts +35 -0
  70. package/dist/bin/notify.d.ts.map +1 -0
  71. package/dist/bin/notify.js +131 -0
  72. package/dist/bin/notify.js.map +1 -0
  73. package/dist/bin/run-tests.d.ts +21 -0
  74. package/dist/bin/run-tests.d.ts.map +1 -0
  75. package/dist/bin/run-tests.js +138 -0
  76. package/dist/bin/run-tests.js.map +1 -0
  77. package/dist/bin/slack.d.ts +2 -14
  78. package/dist/bin/slack.d.ts.map +1 -1
  79. package/dist/bin/slack.js +17 -46
  80. package/dist/bin/slack.js.map +1 -1
  81. package/dist/bin/stdin-input.d.ts +79 -0
  82. package/dist/bin/stdin-input.d.ts.map +1 -0
  83. package/dist/bin/stdin-input.js +124 -0
  84. package/dist/bin/stdin-input.js.map +1 -0
  85. package/dist/bin/test-files-glob.d.ts +18 -0
  86. package/dist/bin/test-files-glob.d.ts.map +1 -0
  87. package/dist/bin/test-files-glob.js +108 -0
  88. package/dist/bin/test-files-glob.js.map +1 -0
  89. package/dist/bin/test-report.d.ts +69 -8
  90. package/dist/bin/test-report.d.ts.map +1 -1
  91. package/dist/bin/test-report.js +76 -67
  92. package/dist/bin/test-report.js.map +1 -1
  93. package/dist/bin/types.d.ts +4 -7
  94. package/dist/bin/types.d.ts.map +1 -1
  95. package/dist/bin/utils/config/load-config.d.ts +8 -0
  96. package/dist/bin/utils/config/load-config.d.ts.map +1 -1
  97. package/dist/bin/utils/config/load-config.js +14 -1
  98. package/dist/bin/utils/config/load-config.js.map +1 -1
  99. package/dist/bin/utils/config/parser.d.ts +15 -2
  100. package/dist/bin/utils/config/parser.d.ts.map +1 -1
  101. package/dist/bin/utils/config/parser.js +29 -8
  102. package/dist/bin/utils/config/parser.js.map +1 -1
  103. package/dist/bin/utils/config/structures/grouped.js +2 -2
  104. package/dist/bin/utils/config/structures/grouped.js.map +1 -1
  105. package/dist/bin/utils/config/structures/legacy.js +1 -1
  106. package/dist/bin/utils/config/structures/legacy.js.map +1 -1
  107. package/dist/lib/extend-expect.d.ts.map +1 -1
  108. package/dist/lib/extend-expect.js +37 -218
  109. package/dist/lib/extend-expect.js.map +1 -1
  110. package/dist/lib/lib.d.ts +1 -5
  111. package/dist/lib/lib.d.ts.map +1 -1
  112. package/dist/lib/lib.js +19 -31
  113. package/dist/lib/lib.js.map +1 -1
  114. package/dist/lib/run-checks.d.ts +14 -0
  115. package/dist/lib/run-checks.d.ts.map +1 -0
  116. package/dist/lib/run-checks.js +82 -0
  117. package/dist/lib/run-checks.js.map +1 -0
  118. package/dist/lib/run-test-result.d.ts +3 -6
  119. package/dist/lib/run-test-result.d.ts.map +1 -1
  120. package/dist/lib/run-test-result.js +41 -54
  121. package/dist/lib/run-test-result.js.map +1 -1
  122. package/dist/test/bundle/cli-bundle.test.js +76 -56
  123. package/dist/test/bundle/cli-bundle.test.js.map +1 -1
  124. package/dist/test/bundle/token-wrapper.test.d.ts +2 -0
  125. package/dist/test/bundle/token-wrapper.test.d.ts.map +1 -0
  126. package/dist/test/bundle/token-wrapper.test.js +80 -0
  127. package/dist/test/bundle/token-wrapper.test.js.map +1 -0
  128. package/dist/test/e2e/cli-bundle.e2e.test.js +33 -68
  129. package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
  130. package/dist/test/e2e/git-build-release.e2e.test.d.ts +2 -0
  131. package/dist/test/e2e/git-build-release.e2e.test.d.ts.map +1 -0
  132. package/dist/test/e2e/git-build-release.e2e.test.js +87 -0
  133. package/dist/test/e2e/git-build-release.e2e.test.js.map +1 -0
  134. package/dist/test/e2e/helpers.d.ts +37 -0
  135. package/dist/test/e2e/helpers.d.ts.map +1 -0
  136. package/dist/test/e2e/helpers.js +57 -0
  137. package/dist/test/e2e/helpers.js.map +1 -0
  138. package/dist/test/unit/bin/actor-filtering.test.js +7 -0
  139. package/dist/test/unit/bin/actor-filtering.test.js.map +1 -1
  140. package/dist/test/unit/bin/actor-ids.test.d.ts +2 -0
  141. package/dist/test/unit/bin/actor-ids.test.d.ts.map +1 -0
  142. package/dist/test/unit/bin/actor-ids.test.js +40 -0
  143. package/dist/test/unit/bin/actor-ids.test.js.map +1 -0
  144. package/dist/test/unit/bin/affected-actors-cli.test.d.ts +2 -0
  145. package/dist/test/unit/bin/affected-actors-cli.test.d.ts.map +1 -0
  146. package/dist/test/unit/bin/affected-actors-cli.test.js +224 -0
  147. package/dist/test/unit/bin/affected-actors-cli.test.js.map +1 -0
  148. package/dist/test/unit/bin/affected-actors.test.d.ts +2 -0
  149. package/dist/test/unit/bin/affected-actors.test.d.ts.map +1 -0
  150. package/dist/test/unit/bin/affected-actors.test.js +153 -0
  151. package/dist/test/unit/bin/affected-actors.test.js.map +1 -0
  152. package/dist/test/unit/bin/build-cli.test.d.ts +2 -0
  153. package/dist/test/unit/bin/build-cli.test.d.ts.map +1 -0
  154. package/dist/test/unit/bin/build-cli.test.js +49 -0
  155. package/dist/test/unit/bin/build-cli.test.js.map +1 -0
  156. package/dist/test/unit/bin/build-from-local.test.js +12 -0
  157. package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
  158. package/dist/test/unit/bin/build.test.js +116 -6
  159. package/dist/test/unit/bin/build.test.js.map +1 -1
  160. package/dist/test/unit/bin/compiled-cli.d.ts +6 -0
  161. package/dist/test/unit/bin/compiled-cli.d.ts.map +1 -0
  162. package/dist/test/unit/bin/compiled-cli.js +23 -0
  163. package/dist/test/unit/bin/compiled-cli.js.map +1 -0
  164. package/dist/test/unit/bin/diff-changes.test.js +168 -114
  165. package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
  166. package/dist/test/unit/bin/diff.test.js +25 -28
  167. package/dist/test/unit/bin/diff.test.js.map +1 -1
  168. package/dist/test/unit/bin/git.test.js +67 -288
  169. package/dist/test/unit/bin/git.test.js.map +1 -1
  170. package/dist/test/unit/bin/notify.test.d.ts +2 -0
  171. package/dist/test/unit/bin/notify.test.d.ts.map +1 -0
  172. package/dist/test/unit/bin/notify.test.js +222 -0
  173. package/dist/test/unit/bin/notify.test.js.map +1 -0
  174. package/dist/test/unit/bin/stdin-input.test.d.ts +2 -0
  175. package/dist/test/unit/bin/stdin-input.test.d.ts.map +1 -0
  176. package/dist/test/unit/bin/stdin-input.test.js +92 -0
  177. package/dist/test/unit/bin/stdin-input.test.js.map +1 -0
  178. package/dist/test/unit/bin/test-cli.test.d.ts +2 -0
  179. package/dist/test/unit/bin/test-cli.test.d.ts.map +1 -0
  180. package/dist/test/unit/bin/test-cli.test.js +146 -0
  181. package/dist/test/unit/bin/test-cli.test.js.map +1 -0
  182. package/dist/test/unit/bin/test-files-glob.test.d.ts +2 -0
  183. package/dist/test/unit/bin/test-files-glob.test.d.ts.map +1 -0
  184. package/dist/test/unit/bin/test-files-glob.test.js +102 -0
  185. package/dist/test/unit/bin/test-files-glob.test.js.map +1 -0
  186. package/dist/test/unit/bin/test-report.test.d.ts +2 -0
  187. package/dist/test/unit/bin/test-report.test.d.ts.map +1 -0
  188. package/dist/test/unit/bin/test-report.test.js +100 -0
  189. package/dist/test/unit/bin/test-report.test.js.map +1 -0
  190. package/dist/test/unit/bin/utils/config/load-config.test.js +19 -1
  191. package/dist/test/unit/bin/utils/config/load-config.test.js.map +1 -1
  192. package/dist/test/unit/bin/utils/config/parser.test.js +36 -4
  193. package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
  194. package/dist/test/unit/bin/utils/config/structures/grouped.test.js +6 -0
  195. package/dist/test/unit/bin/utils/config/structures/grouped.test.js.map +1 -1
  196. package/dist/test/unit/bin/utils/config/structures/legacy.test.js +1 -0
  197. package/dist/test/unit/bin/utils/config/structures/legacy.test.js.map +1 -1
  198. package/dist/test/unit/github-scripts/mask-config-secrets.test.d.ts +2 -0
  199. package/dist/test/unit/github-scripts/mask-config-secrets.test.d.ts.map +1 -0
  200. package/dist/test/unit/github-scripts/mask-config-secrets.test.js +50 -0
  201. package/dist/test/unit/github-scripts/mask-config-secrets.test.js.map +1 -0
  202. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.d.ts +2 -0
  203. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.d.ts.map +1 -0
  204. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.js +102 -0
  205. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.js.map +1 -0
  206. package/dist/test/unit/run-checks.test.d.ts +2 -0
  207. package/dist/test/unit/run-checks.test.d.ts.map +1 -0
  208. package/dist/test/unit/run-checks.test.js +62 -0
  209. package/dist/test/unit/run-checks.test.js.map +1 -0
  210. package/dist/test/unit/run-test-result.test.d.ts +2 -0
  211. package/dist/test/unit/run-test-result.test.d.ts.map +1 -0
  212. package/dist/test/unit/run-test-result.test.js +34 -0
  213. package/dist/test/unit/run-test-result.test.js.map +1 -0
  214. package/dist/test/unit/should-built-and-test.test.js +41 -38
  215. package/dist/test/unit/should-built-and-test.test.js.map +1 -1
  216. package/dist/tsconfig.tsbuildinfo +1 -1
  217. package/dist/workflow-scripts-src/run-with-apify-tokens.d.ts +3 -0
  218. package/dist/workflow-scripts-src/run-with-apify-tokens.d.ts.map +1 -0
  219. package/dist/workflow-scripts-src/run-with-apify-tokens.js +100 -0
  220. package/dist/workflow-scripts-src/run-with-apify-tokens.js.map +1 -0
  221. package/docs/cli.md +469 -0
  222. package/docs/lib.md +276 -0
  223. package/docs/workflows.md +455 -0
  224. package/eslint.config.mjs +10 -1
  225. package/lib/extend-expect.ts +48 -258
  226. package/lib/lib.ts +25 -34
  227. package/lib/run-checks.ts +101 -0
  228. package/lib/run-test-result.ts +45 -55
  229. package/package.json +4 -2
  230. package/platform-tests-investigate-and-fix/skills/assess-impact/SKILL.md +42 -0
  231. package/platform-tests-investigate-and-fix/skills/measure-flakiness/SKILL.md +4 -2
  232. package/test/bundle/cli-bundle.test.ts +97 -67
  233. package/test/bundle/token-wrapper.test.ts +110 -0
  234. package/test/e2e/cli-bundle.e2e.test.ts +60 -93
  235. package/test/e2e/git-build-release.e2e.test.ts +113 -0
  236. package/test/e2e/helpers.ts +82 -0
  237. package/test/unit/bin/actor-filtering.test.ts +8 -0
  238. package/test/unit/bin/actor-ids.test.ts +47 -0
  239. package/test/unit/bin/affected-actors-cli.test.ts +271 -0
  240. package/test/unit/bin/affected-actors.test.ts +221 -0
  241. package/test/unit/bin/build-cli.test.ts +61 -0
  242. package/test/unit/bin/build-from-local.test.ts +16 -0
  243. package/test/unit/bin/build.test.ts +149 -5
  244. package/test/unit/bin/compiled-cli.ts +23 -0
  245. package/test/unit/bin/diff-changes.test.ts +181 -114
  246. package/test/unit/bin/diff.test.ts +25 -28
  247. package/test/unit/bin/git.test.ts +76 -361
  248. package/test/unit/bin/notify.test.ts +299 -0
  249. package/test/unit/bin/stdin-input.test.ts +120 -0
  250. package/test/unit/bin/test-cli.test.ts +175 -0
  251. package/test/unit/bin/test-files-glob.test.ts +118 -0
  252. package/test/unit/bin/test-report.test.ts +118 -0
  253. package/test/unit/bin/utils/config/load-config.test.ts +26 -1
  254. package/test/unit/bin/utils/config/parser.test.ts +46 -5
  255. package/test/unit/bin/utils/config/structures/grouped.test.ts +12 -0
  256. package/test/unit/bin/utils/config/structures/legacy.test.ts +1 -0
  257. package/test/unit/github-scripts/mask-config-secrets.test.ts +61 -0
  258. package/test/unit/github-scripts/run-with-apify-tokens.test.ts +115 -0
  259. package/test/unit/run-checks.test.ts +76 -0
  260. package/test/unit/run-test-result.test.ts +44 -0
  261. package/test/unit/should-built-and-test.test.ts +46 -39
  262. package/tsconfig.json +1 -1
  263. package/vitest.config.ts +9 -0
  264. package/workflow-scripts-src/run-with-apify-tokens.ts +112 -0
  265. package/.github/scripts/check-major-tag-refs.mjs +0 -72
  266. package/dist/test/unit/bin/logging.test.d.ts +0 -2
  267. package/dist/test/unit/bin/logging.test.d.ts.map +0 -1
  268. package/dist/test/unit/bin/logging.test.js +0 -43
  269. package/dist/test/unit/bin/logging.test.js.map +0 -1
  270. package/dist/test/unit/bin/slack.test.d.ts +0 -2
  271. package/dist/test/unit/bin/slack.test.d.ts.map +0 -1
  272. package/dist/test/unit/bin/slack.test.js +0 -54
  273. package/dist/test/unit/bin/slack.test.js.map +0 -1
  274. package/test/unit/bin/logging.test.ts +0 -52
  275. package/test/unit/bin/slack.test.ts +0 -65
package/docs/lib.md ADDED
@@ -0,0 +1,276 @@
1
+ # Vitest library API
2
+
3
+ ```ts
4
+ import { describe, testActor, testStandbyActor, RunTestResult, ExpectStatic } from 'apify-test-tools';
5
+ ```
6
+
7
+ Requires `vitest` `3.2.4` or later as a peer dependency.
8
+
9
+ ## Environment variables
10
+
11
+ The library reads these when the test file is loaded:
12
+
13
+ | Variable | Description |
14
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
15
+ | `TESTER_APIFY_TOKEN` | Token the tests start runs and read their data with. Without it, every `describe` block is skipped, so a plain `npm test` stays unaffected. |
16
+ | `ACTOR_BUILDS` | JSON array of Actors to test, e.g. the builds the CLI's [`build`](cli.md#building-actors-build) and [`build-from-local`](cli.md#build-from-local-source-build-from-local) print. Only tests of Actors listed here run, against the listed build, or the default build for an Actor listed without one. |
17
+
18
+ `ACTOR_BUILDS` entries look like this, and are matched to tests by `actorFullName` or `actorRawId`:
19
+
20
+ ```json
21
+ [{ "buildId": "...", "actorRawId": "...", "buildNumber": "1.2.3", "actorFullName": "john.doe/my-actor" }]
22
+ ```
23
+
24
+ ## `describe(name, fn, options?)`
25
+
26
+ Same as vitest's `describe`, but skipped unless `TESTER_APIFY_TOKEN` is set. Its tests
27
+ run concurrently by default. Use it instead of vitest's `describe` for platform tests.
28
+
29
+ ## `testActor(actorId, testName, fn, options?)`
30
+
31
+ Registers a test for an Actor. `actorId` is the Actor's full name (`owner/name`) or its raw platform ID. The test
32
+ only runs when the Actor is in `ACTOR_BUILDS`. Its name is
33
+ `<actorId>: <testName>`.
34
+
35
+ `fn` gets the vitest test context, with `expect` extended by the [custom matchers](#custom-matchers), and a `run`
36
+ function.
37
+
38
+ `expect` is soft: a failed assertion marks the test as failed but the test keeps going, so one run reports every
39
+ failed check at once. Use `expect.hard(...)` for an assertion that must stop the test, e.g. before indexing into a
40
+ list that may be empty.
41
+
42
+ ```ts
43
+ import { describe, testActor } from 'apify-test-tools';
44
+
45
+ describe('test', () => {
46
+ testActor(actorId, 'actor test 1', async ({ expect, run }) => {
47
+ const runResult = await run({ input });
48
+
49
+ // your checks
50
+ });
51
+
52
+ testActor(actorId, 'actor test 2', async ({ expect, run }) => {
53
+ const runResult = await run({ input });
54
+
55
+ // your checks
56
+ });
57
+ });
58
+ ```
59
+
60
+ ### Test options
61
+
62
+ The fourth argument takes vitest's test options:
63
+
64
+ | Option | Default |
65
+ | ------------ | ------- |
66
+ | `timeout` | 1 hour |
67
+ | `retry` | `1` |
68
+ | `concurrent` | `true` |
69
+
70
+ ```ts
71
+ testActor(
72
+ actorId,
73
+ 'slow actor test',
74
+ async ({ expect, run }) => {
75
+ const runResult = await run({ input });
76
+ await expect(runResult).toFinishWith({ datasetItemCount: 100 });
77
+ },
78
+ {
79
+ timeout: 2 * 60 * 60 * 1000, // 2 hours
80
+ retry: 3,
81
+ },
82
+ );
83
+ ```
84
+
85
+ ### `run(options)`
86
+
87
+ Starts the Actor, waits for the run to finish, and returns a [`RunTestResult`](#runtestresult). It runs the build
88
+ from `ACTOR_BUILDS`, or the Actor's default build when it isn't listed there. Before returning, it waits 20 seconds
89
+ for the run's dataset and statistics to sync, since the platform is only eventually consistent. The run's Console
90
+ link is added to the test as a vitest annotation.
91
+
92
+ | Option | Description |
93
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
94
+ | `input` | Actor input |
95
+ | `prefilledInput` | When `true`, `input` is merged on top of the Actor's prefilled input |
96
+ | `options` | [Actor call options](https://docs.apify.com/api/client/js/reference/interface/ActorCallOptions) like `memory`. `timeout` is in seconds and defaults to the Actor's default run timeout, at most 59 minutes, so the run finishes before the test times out |
97
+ | `runId` | Don't start a run; return this existing run instead. Every other option is ignored |
98
+
99
+ #### Using prefilled input
100
+
101
+ ```ts
102
+ testActor(actorId, 'with prefilled input', async ({ expect, run }) => {
103
+ const runResult = await run({
104
+ prefilledInput: true,
105
+ input: { maxItems: 10 }, // merged on top of the prefilled input
106
+ });
107
+ await expect(runResult).toFinishWith({ datasetItemCount: 10 });
108
+ });
109
+ ```
110
+
111
+ #### Testing an existing run
112
+
113
+ Useful for iterating on the checks of a test without waiting for a new run:
114
+
115
+ ```ts
116
+ testActor(actorId, 'validate existing run', async ({ expect, run }) => {
117
+ const runResult = await run({ runId: 'some-run-id' });
118
+ await expect(runResult).toFinishWith({ datasetItemCount: 100 });
119
+ });
120
+ ```
121
+
122
+ ## `testStandbyActor(actorId, testName, fn, options?)`
123
+
124
+ Same as `testActor`, for Actors in standby mode. It creates a temporary task with the build from `ACTOR_BUILDS` (or
125
+ the Actor's standby build) and deletes it after the test. Instead of `run`, `fn` gets `callStandby`, which sends a
126
+ `POST` request with `input` as the JSON body to the task's standby URL, plus `path`, and returns
127
+ `{ data, status, headers }`, where `data` is the parsed JSON response.
128
+
129
+ ```ts
130
+ import { describe, testStandbyActor } from 'apify-test-tools';
131
+
132
+ describe('standby tests', () => {
133
+ testStandbyActor(actorId, 'standby request', async ({ expect, callStandby }) => {
134
+ const { data, status } = await callStandby({
135
+ input: { query: 'test' },
136
+ path: '/search',
137
+ headers: { 'Content-Type': 'application/json' },
138
+ });
139
+
140
+ expect(status).toBe(200);
141
+ expect(data.results).toBeNonEmptyArray();
142
+ });
143
+ });
144
+ ```
145
+
146
+ It fails if the Actor doesn't support standby mode.
147
+
148
+ ## `RunTestResult`
149
+
150
+ What `run` returns. Every getter fetches its data on the first call and returns the same result afterwards, so you
151
+ can call them freely.
152
+
153
+ | Member | Returns |
154
+ | -------------------------- | -------------------------------------------------------------------------------------------------------- |
155
+ | `id` | The run ID |
156
+ | `status` | The run status when it finished |
157
+ | `getDataset<T>()` | `{ items: T[] }`, every item of the default dataset |
158
+ | `getLog()` | The run log as a string |
159
+ | `getStatistics()` | The crawler statistics from the `SDK_CRAWLER_STATISTICS_0` record, or `undefined` for non-crawler Actors |
160
+ | `getInput<T>()` | The `INPUT` record of the default key-value store |
161
+ | `getKeyValueStoreClient()` | An `apify-client` client for the default key-value store |
162
+ | `getRunInfo()` | The run object, fetched again so it reflects changes after the run finished |
163
+
164
+ ```ts
165
+ testActor(actorId, 'check dataset items', async ({ expect, run }) => {
166
+ const runResult = await run({ input });
167
+
168
+ const { items } = await runResult.getDataset();
169
+ expect(items[0].title).toBeNonEmptyString();
170
+
171
+ const log = await runResult.getLog();
172
+ expect(log).toContain('Crawl finished');
173
+
174
+ const stats = await runResult.getStatistics();
175
+ expect(stats?.requestsFinished).toBeGreaterThan(0);
176
+
177
+ const kvs = runResult.getKeyValueStoreClient();
178
+ const record = await kvs.getRecord('OUTPUT');
179
+
180
+ const runInfo = await runResult.getRunInfo();
181
+ });
182
+ ```
183
+
184
+ ## `toFinishWith(options)`
185
+
186
+ Validates the common run properties in a single call and reports every failed check at once. Only
187
+ `datasetItemCount` is required; the rest have defaults. Numeric checks take an exact number or a range: `{ min }`,
188
+ `{ max }` or `{ min, max }`. Set a check with a default to `null` to skip it.
189
+
190
+ | Option | Default | Checks |
191
+ | ---------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------ |
192
+ | `datasetItemCount` | required | Number of items in the default dataset |
193
+ | `status` | `'SUCCEEDED'` | Run status |
194
+ | `duration` | `{ min: 600, max: 600_000 }` | Run duration in milliseconds (0.6 s to 10 min) |
195
+ | `failedRequests` | `0` | Failed requests in the crawler statistics |
196
+ | `requestsRetries` | `{ max: 3 }` | Request retries in the crawler statistics |
197
+ | `maxRetriesPerRequest` | `null` | The most retries any single request needed |
198
+ | `forbiddenLogs` | `['ReferenceError', 'TypeError']` | Strings that must not appear in the log |
199
+ | `chargedEventCounts` | not checked | Pay-per-event counts, per event name. An event you omit is expected to have been charged 0 times |
200
+
201
+ `failedRequests` and `requestsRetries` come from the crawler statistics, so they fail for an Actor that doesn't
202
+ write them. Set both to `null` for Actors that don't use a Crawlee crawler.
203
+
204
+ ```ts
205
+ await expect(runResult).toFinishWith({
206
+ datasetItemCount: { min: 80, max: 120 },
207
+ duration: { max: 30 * 60 * 1000 },
208
+ chargedEventCounts: {
209
+ 'actor-start': 1,
210
+ 'place-scraped': { min: 80, max: 120 },
211
+ },
212
+ });
213
+ ```
214
+
215
+ ## Custom matchers
216
+
217
+ `expect` in `testActor` and `testStandbyActor` is extended with:
218
+
219
+ - `toBeArray()` / `toBeEmptyArray()` / `toBeNonEmptyArray()`
220
+ - `toBeString()` / `toBeNonEmptyString()` / `toStartWith(prefix)`
221
+ - `toBeNumber()` / `toBeWholeNumber()` / `toBeWithinRange(min, max)`
222
+ - `toBeBoolean()` / `toBeTrue()` / `toBeFalse()`
223
+ - `toBeObject()` / `toBeNonEmptyObject()`
224
+ - [`toFinishWith(options)`](#tofinishwithoptions)
225
+
226
+ Pass a message as the second argument of `expect` to say what failed:
227
+
228
+ ```ts
229
+ expect(place.title, `London Eye's title`).toEqual('lastminute.com London Eye');
230
+ ```
231
+
232
+ ### Custom validation functions
233
+
234
+ Wrap shared checks in your own functions, e.g. in `test/platform/utils.ts`, and pass them `expect`. Use the
235
+ `ExpectStatic` type so the custom matchers type-check:
236
+
237
+ ```ts
238
+ import { ExpectStatic } from 'apify-test-tools';
239
+
240
+ export const validateItem = (expect: ExpectStatic, item: any) => {
241
+ expect(item.title, 'Item title').toBeString();
242
+ };
243
+ ```
244
+
245
+ ## Running tests locally
246
+
247
+ Run every command from the root of the Actor repository.
248
+
249
+ **Against the current default builds.** Enough when you only change the tests. List the Actors with
250
+ [`get-actor-configs`](cli.md#listing-actors-get-actor-configs), all of them or a few with `--actors`, and pass them to
251
+ [`test`](cli.md#running-tests-test), which tests each on its default build:
252
+
253
+ ```bash
254
+ npx apify-test-tools get-actor-configs --actors john.doe/my-actor | TESTER_APIFY_TOKEN=<token> npx apify-test-tools test
255
+ ```
256
+
257
+ This also runs a test that reuses an existing run with [`runId`](#testing-an-existing-run), as long as its Actor is
258
+ listed.
259
+
260
+ **Against builds of your changes.** Build the Actors with the CLI, either from local files with
261
+ [`build-from-local`](cli.md#build-from-local-source-build-from-local) or from a pushed branch with
262
+ [`build`](cli.md#building-actors-build), and pass the output to [`test`](cli.md#running-tests-test):
263
+
264
+ ```bash
265
+ APIFY_TOKEN_JOHN_DOE=<token> npx apify-test-tools build-from-local --actors john.doe/my-actor > builds.json
266
+
267
+ TESTER_APIFY_TOKEN=<token> npx apify-test-tools test < builds.json
268
+ ```
269
+
270
+ `test` passes the builds to vitest as `ACTOR_BUILDS`, so plain vitest works too:
271
+ `ACTOR_BUILDS="$(cat builds.json)" TESTER_APIFY_TOKEN=<token> npx vitest run test/platform`.
272
+
273
+ You can change the tests and re-run them against the same builds.
274
+
275
+ `TESTER_APIFY_TOKEN` can be the token of any account with enough memory for the runs. To run only some tests, pass a specific file instead of `test/platform`, or filter by
276
+ name with vitest's `-t <name>` when running vitest directly. See [Running locally](cli.md#running-locally) for the full flow.