apify-test-tools 0.10.2-beta.19 → 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.
- package/.github/cli/apify-test-tools.mjs +114 -114
- package/.github/scripts/run-with-apify-tokens.mjs +6 -6
- package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +1 -1
- package/.github/workflows/public_platform-tests.yaml +6 -8
- package/.github/workflows/public_pr-build-test.yaml +11 -11
- package/.github/workflows/public_push-build-latest.yaml +1 -0
- package/CHANGELOG.md +1 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +10 -6
- package/bin/affected-actors.ts +20 -7
- package/bin/diff-changes.ts +40 -16
- package/bin/main.ts +12 -19
- package/bin/run-tests.ts +29 -4
- package/bin/test-files-glob.ts +126 -0
- package/bin/utils/config/load-config.ts +4 -1
- package/bin/utils/config/parser.ts +24 -1
- package/dist/bin/affected-actors.d.ts +13 -4
- package/dist/bin/affected-actors.d.ts.map +1 -1
- package/dist/bin/affected-actors.js +11 -4
- package/dist/bin/affected-actors.js.map +1 -1
- package/dist/bin/diff-changes.d.ts +12 -3
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +25 -12
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/main.js +11 -17
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/run-tests.d.ts +3 -3
- package/dist/bin/run-tests.d.ts.map +1 -1
- package/dist/bin/run-tests.js +24 -2
- package/dist/bin/run-tests.js.map +1 -1
- package/dist/bin/test-files-glob.d.ts +18 -0
- package/dist/bin/test-files-glob.d.ts.map +1 -0
- package/dist/bin/test-files-glob.js +108 -0
- package/dist/bin/test-files-glob.js.map +1 -0
- package/dist/bin/utils/config/load-config.d.ts +3 -0
- package/dist/bin/utils/config/load-config.d.ts.map +1 -1
- package/dist/bin/utils/config/load-config.js +3 -1
- package/dist/bin/utils/config/load-config.js.map +1 -1
- package/dist/bin/utils/config/parser.d.ts +9 -0
- package/dist/bin/utils/config/parser.d.ts.map +1 -1
- package/dist/bin/utils/config/parser.js +13 -1
- package/dist/bin/utils/config/parser.js.map +1 -1
- package/dist/test/bundle/cli-bundle.test.js +1 -1
- package/dist/test/bundle/cli-bundle.test.js.map +1 -1
- package/dist/test/e2e/cli-bundle.e2e.test.js +1 -1
- package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
- package/dist/test/unit/bin/actor-filtering.test.js +7 -0
- package/dist/test/unit/bin/actor-filtering.test.js.map +1 -1
- package/dist/test/unit/bin/affected-actors-cli.test.js +59 -2
- package/dist/test/unit/bin/affected-actors-cli.test.js.map +1 -1
- package/dist/test/unit/bin/affected-actors.test.js +18 -6
- package/dist/test/unit/bin/affected-actors.test.js.map +1 -1
- package/dist/test/unit/bin/diff-changes.test.js +44 -19
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/test-cli.test.js +34 -3
- package/dist/test/unit/bin/test-cli.test.js.map +1 -1
- package/dist/test/unit/bin/test-files-glob.test.d.ts +2 -0
- package/dist/test/unit/bin/test-files-glob.test.d.ts.map +1 -0
- package/dist/test/unit/bin/test-files-glob.test.js +102 -0
- package/dist/test/unit/bin/test-files-glob.test.js.map +1 -0
- package/dist/test/unit/bin/utils/config/parser.test.js +16 -1
- package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/cli.md +46 -29
- package/docs/workflows.md +53 -20
- package/package.json +1 -1
- package/test/bundle/cli-bundle.test.ts +1 -1
- package/test/e2e/cli-bundle.e2e.test.ts +9 -5
- package/test/unit/bin/actor-filtering.test.ts +8 -0
- package/test/unit/bin/affected-actors-cli.test.ts +73 -3
- package/test/unit/bin/affected-actors.test.ts +49 -10
- package/test/unit/bin/diff-changes.test.ts +48 -19
- package/test/unit/bin/test-cli.test.ts +41 -3
- package/test/unit/bin/test-files-glob.test.ts +118 -0
- package/test/unit/bin/utils/config/parser.test.ts +24 -1
package/docs/cli.md
CHANGED
|
@@ -71,10 +71,12 @@ changed files. For each changed file, for each Actor:
|
|
|
71
71
|
get no special treatment: to treat them as top-level, list them in `overrideActorContext`.
|
|
72
72
|
5. **`.dockerignore` filtering** — if a `.dockerignore` exists at the root of the Actor's `dockerContextDir`,
|
|
73
73
|
matching files are ignored. Patterns are resolved relative to `dockerContextDir`, matching Docker's own behavior.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
Platform test files are the exception, see the next step.
|
|
75
|
+
**Platform test files** — a file that matches `platformTestsGlob` from the config file (a glob from the repo root,
|
|
76
|
+
default `test/platform/**`, helpers included) is a `test` change, also when `.dockerignore` lists it. This is
|
|
77
|
+
checked before the `.dockerignore` filtering. A test change puts the Actor in `testChangedActors`, so its tests
|
|
78
|
+
run, but it is neither functional nor cosmetic, so a release doesn't build it. Context matching still applies, so
|
|
79
|
+
tests outside an Actor's `dockerContextDir` (e.g. a root `test/` with standalone Actors) need to be listed in its
|
|
78
80
|
`overrideActorContext`; `get-affected-actors` logs a warning for changed test files outside every Actor's context.
|
|
79
81
|
6. **README classification** — a `README.md` file is `cosmetic` (only triggers a release build, not tests) if it
|
|
80
82
|
lives inside the Actor's own `folder`; otherwise it's ignored entirely, since it isn't documentation for this
|
|
@@ -97,13 +99,12 @@ npx apify-test-tools get-affected-actors --base origin/master --since <sha>
|
|
|
97
99
|
npx apify-test-tools get-affected-actors --since <sha>
|
|
98
100
|
```
|
|
99
101
|
|
|
100
|
-
| Flag
|
|
101
|
-
|
|
|
102
|
-
| `--base`
|
|
103
|
-
| `--since`
|
|
104
|
-
| `--
|
|
105
|
-
| `--
|
|
106
|
-
| `--ignore` | none | Never report these Actors |
|
|
102
|
+
| Flag | Default | Meaning |
|
|
103
|
+
| ---------- | ------- | -------------------------------------------------------------------------------------- |
|
|
104
|
+
| `--base` | | Only what `HEAD` changes compared to this branch counts, like `git diff <base>...HEAD` |
|
|
105
|
+
| `--since` | | Only changes after this commit count (see below) |
|
|
106
|
+
| `--actors` | all | Only report these Actors |
|
|
107
|
+
| `--ignore` | none | Never report these Actors |
|
|
107
108
|
|
|
108
109
|
Pass `--base`, `--since`, or both:
|
|
109
110
|
|
|
@@ -133,26 +134,33 @@ The output is a JSON document:
|
|
|
133
134
|
"changedFiles": ["src/main.ts", "CHANGELOG.md"],
|
|
134
135
|
"changelog": "Lines added to CHANGELOG.md, or null",
|
|
135
136
|
"functionallyChangedActors": [{ "actorFullName": "john.doe/my-actor" }],
|
|
136
|
-
"cosmeticallyChangedActors": [{ "actorFullName": "john.doe/other-actor" }]
|
|
137
|
+
"cosmeticallyChangedActors": [{ "actorFullName": "john.doe/other-actor" }],
|
|
138
|
+
"testChangedActors": [{ "actorFullName": "john.doe/my-actor" }]
|
|
137
139
|
}
|
|
138
140
|
```
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
`
|
|
142
|
+
`functionallyChangedActors` have at least one functional change, so they need a new build and tests.
|
|
143
|
+
`cosmeticallyChangedActors` have cosmetic changes and no functional one (see [Change detection](#change-detection)), so
|
|
144
|
+
they need a new build only when released. An Actor is in at most one of these two lists. `testChangedActors` have a
|
|
145
|
+
changed platform test (`platformTestsGlob` in the config) in their context, whatever else changed, so their tests need
|
|
146
|
+
to run. A test change alone needs no release. The PR workflow builds `functionallyChangedActors` and
|
|
147
|
+
`testChangedActors`. The release workflow builds `functionallyChangedActors` and `cosmeticallyChangedActors`.
|
|
148
|
+
|
|
149
|
+
With `--base` and `--since`, an Actor is in `testChangedActors` when its tests changed on the branch and again since
|
|
150
|
+
`--since`, the same rule as for functional changes.
|
|
144
151
|
|
|
145
152
|
## Building Actors (`build`)
|
|
146
153
|
|
|
147
154
|
`build` builds exactly the Actors it is given on stdin, from the checked-out branch as it is on the remote, and
|
|
148
|
-
prints the builds as the same JSON array `build-from-local` prints.
|
|
149
|
-
|
|
155
|
+
prints the builds as the same JSON array `build-from-local` prints. An Actor listed twice is built once, so the lists
|
|
156
|
+
of `get-affected-actors` can be joined without removing duplicates first. A detached HEAD is an error, since there is
|
|
157
|
+
no branch to build from. It doesn't look at git history: which Actors to build is up to you, usually picked from the
|
|
150
158
|
output of [`get-affected-actors`](#finding-affected-actors-get-affected-actors).
|
|
151
159
|
|
|
152
160
|
```bash
|
|
153
|
-
# A PR: the Actors
|
|
161
|
+
# A PR: the Actors whose code or platform tests the branch changed, built into the test version
|
|
154
162
|
npx apify-test-tools get-affected-actors --base origin/master \
|
|
155
|
-
| jq '{ actors: .functionallyChangedActors }' \
|
|
163
|
+
| jq '{ actors: (.functionallyChangedActors + .testChangedActors) }' \
|
|
156
164
|
| npx apify-test-tools build --version-number 0.99
|
|
157
165
|
|
|
158
166
|
# A release: every Actor changed since the last release, into the version users run
|
|
@@ -285,7 +293,8 @@ the repository locked) on the Actors it is given on stdin, and prints a report o
|
|
|
285
293
|
... | npx apify-test-tools build --version-number 0.99 | npx apify-test-tools test > test-report.json
|
|
286
294
|
|
|
287
295
|
# Scheduled tests: every Actor in the config, on its default build
|
|
288
|
-
npx apify-test-tools get-actor-configs
|
|
296
|
+
npx apify-test-tools get-actor-configs \
|
|
297
|
+
| npx apify-test-tools test --test-files-glob 'test/platform/{hourly,hourly-without-pr}/**' > test-report.json
|
|
289
298
|
|
|
290
299
|
# Only some of them
|
|
291
300
|
npx apify-test-tools get-actor-configs --actors john.doe/my-actor | npx apify-test-tools test > test-report.json
|
|
@@ -309,11 +318,19 @@ annotate the failing tests in a GitHub Actions run. It takes no other vitest opt
|
|
|
309
318
|
test by name, run vitest directly with the builds in `ACTOR_BUILDS` (see
|
|
310
319
|
[Running tests locally](lib.md#running-tests-locally)).
|
|
311
320
|
|
|
312
|
-
|
|
|
313
|
-
| ---------------------- |
|
|
314
|
-
| `
|
|
315
|
-
| `--vitest-output-file` |
|
|
316
|
-
| `--reporter` |
|
|
321
|
+
| Flag | Default | Meaning |
|
|
322
|
+
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
323
|
+
| `--test-files-glob` | `test/platform/**` | Glob from the repo root of the test files to run, see below |
|
|
324
|
+
| `--vitest-output-file` | | Also keep vitest's own JSON report there, e.g. to upload it |
|
|
325
|
+
| `--reporter` | | [Vitest reporter](https://vitest.dev/guide/reporters) to add to `default` and `json`, e.g. `github-actions`. Repeat it for more |
|
|
326
|
+
|
|
327
|
+
`test` takes no test paths as arguments, only `--test-files-glob`. The glob is read the same way as
|
|
328
|
+
`platformTestsGlob` in the config file. Vitest filters are parts of a file path, not globs, so `test` finds the files
|
|
329
|
+
the glob matches and passes vitest as few paths as it can: each folder whose files all match, with a trailing `/`, and
|
|
330
|
+
the other matched files one by one. `test/platform/{daily,hourly}/**` becomes two folders however many tests they
|
|
331
|
+
hold, so the command line stays short. Vitest still applies its own `include` and `exclude`, so helper files in the
|
|
332
|
+
glob run nothing. A glob that matches no file is an error, because vitest without filters would run every test of the
|
|
333
|
+
repo. Quote the glob, so the shell doesn't expand it.
|
|
317
334
|
|
|
318
335
|
Vitest's own output goes to stderr, followed by a summary that lists every test with the link to its run. Stdout
|
|
319
336
|
gets only the report:
|
|
@@ -432,15 +449,15 @@ To build from git, like CI does:
|
|
|
432
449
|
5. Run tests against those builds. You can change tests and run on the same builds.
|
|
433
450
|
|
|
434
451
|
```bash
|
|
435
|
-
npx apify-test-tools get-affected-actors --base origin/master
|
|
436
|
-
| jq '{ actors: .functionallyChangedActors }' \
|
|
452
|
+
npx apify-test-tools get-affected-actors --base origin/master \
|
|
453
|
+
| jq '{ actors: (.functionallyChangedActors + .testChangedActors) }' \
|
|
437
454
|
| APIFY_TOKEN_JOHN_DOE=apify_api_xxx npx apify-test-tools build --version-number 0.99 > builds.json
|
|
438
455
|
```
|
|
439
456
|
|
|
440
457
|
Run it with your dummy branch checked out: it builds the Actors that branch changed functionally, from the branch as
|
|
441
458
|
it is on the remote, into the test version `0.99`. Add `--dry-run` to `build` to print the plan (the version and Git
|
|
442
459
|
URL each Actor would be built from) first, and update `--base` to the branch you compare against. Tests only run for
|
|
443
|
-
Actors in `ACTOR_BUILDS`, so
|
|
460
|
+
Actors in `ACTOR_BUILDS`, so the `jq` filter also builds the Actors whose platform tests changed.
|
|
444
461
|
|
|
445
462
|
If you want to test against existing builds instead, skip the build and write the build array by hand, only for the
|
|
446
463
|
Actors you need to test.
|
package/docs/workflows.md
CHANGED
|
@@ -39,11 +39,12 @@ Validates a pull request. It runs two jobs in parallel.
|
|
|
39
39
|
**`platformTest`** builds and tests the Actors the PR changes:
|
|
40
40
|
|
|
41
41
|
1. Finds the affected Actors with [`get-affected-actors`](cli.md#finding-affected-actors-get-affected-actors),
|
|
42
|
-
comparing the PR branch with its base branch. Changed files
|
|
43
|
-
`.dockerignore` lists them.
|
|
44
|
-
2. Builds the
|
|
45
|
-
Cosmetic-only changes (README, CHANGELOG, schema formatting) need no
|
|
46
|
-
|
|
42
|
+
comparing the PR branch with its base branch. Changed files that match `platformTestsGlob` in the config file
|
|
43
|
+
(default `test/platform/**`) are test changes, also when `.dockerignore` lists them.
|
|
44
|
+
2. Builds the Actors with functional or test changes into the test version `0.99` with
|
|
45
|
+
[`build`](cli.md#building-actors-build). Cosmetic-only changes (README, CHANGELOG, schema formatting) need no
|
|
46
|
+
tests, so they're not built.
|
|
47
|
+
3. Runs [`test`](cli.md#running-tests-test) on the files that match `test-files-glob` with those builds, so only the
|
|
47
48
|
tests of the built Actors run. Failing tests are annotated on the PR.
|
|
48
49
|
4. On success, caches the tested commit as the "last validated commit". The next push to the PR then only builds
|
|
49
50
|
and tests the Actors that changed since it (see `--since` in
|
|
@@ -62,11 +63,11 @@ Validates a pull request. It runs two jobs in parallel.
|
|
|
62
63
|
In a repo whose Actors aren't written in JavaScript or TypeScript, define the `build-check`, `lint` and `test`
|
|
63
64
|
scripts, e.g. to run your language's own checks: without them, the TypeScript, Lint and Test checks fail.
|
|
64
65
|
|
|
65
|
-
| Input | Type | Default
|
|
66
|
-
| -------------------------- | ------- |
|
|
67
|
-
| `skip-platform-tests` | boolean | `false`
|
|
68
|
-
| `test-files-glob` | string | `
|
|
69
|
-
| `require-env-vars-in-sync` | boolean | `true`
|
|
66
|
+
| Input | Type | Default | Description |
|
|
67
|
+
| -------------------------- | ------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `skip-platform-tests` | boolean | `false` | Skip the `platformTest` job and run the static checks only |
|
|
69
|
+
| `test-files-glob` | string | `test/platform/**` | Glob of the test files to run, from the repo root. Empty means the default. See [Splitting the tests](#splitting-the-tests) |
|
|
70
|
+
| `require-env-vars-in-sync` | boolean | `true` | Fail the build unless the env var names of `0.99` match the default version's, see [Env var check](cli.md#env-var-check) |
|
|
70
71
|
|
|
71
72
|
Secrets: `TESTER_APIFY_TOKEN`, the Actor tokens in `CONFIG_SECRETS`, and optionally `NPM_TOKEN`. With
|
|
72
73
|
`skip-platform-tests`, only `NPM_TOKEN`.
|
|
@@ -83,6 +84,9 @@ on:
|
|
|
83
84
|
jobs:
|
|
84
85
|
buildDevelAndTest:
|
|
85
86
|
uses: apify/apify-test-tools/.github/workflows/public_pr-build-test.yaml@workflows-v0
|
|
87
|
+
with:
|
|
88
|
+
# Leaves out the *-without-pr folders, see "Splitting the tests"
|
|
89
|
+
test-files-glob: 'test/platform/{daily,hourly}/**'
|
|
86
90
|
secrets:
|
|
87
91
|
TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
|
|
88
92
|
# One entry per secret apify-test-tools.config.json names, today each `tokenEnvVar`
|
|
@@ -101,7 +105,7 @@ Releases the Actors a push changed. Skipped when the head commit message contain
|
|
|
101
105
|
1. `get-affected-actors --since ${{ github.event.before }}` finds everything the push changed. See
|
|
102
106
|
[Releasing Actors](cli.md#releasing-actors) for how the released range is determined.
|
|
103
107
|
2. `build --version-number default` builds both the functionally and the cosmetically changed Actors into their
|
|
104
|
-
default version, the one users run.
|
|
108
|
+
default version, the one users run. A push that only changes platform tests builds nothing.
|
|
105
109
|
3. [`notify`](cli.md#notifying-notify) posts the release (its changelog additions and its commits, linked to
|
|
106
110
|
GitHub) to the report channel. It posts nothing when the push brought no new commits.
|
|
107
111
|
4. `delete-old-builds` deletes old builds of every Actor in the config (see
|
|
@@ -146,15 +150,15 @@ Runs the platform tests of every Actor in the config against its current default
|
|
|
146
150
|
reaches the tests. Both the report of `test` and
|
|
147
151
|
vitest's own JSON report are uploaded as the `vitest-results` artifact. Runs only on `master` or `main`.
|
|
148
152
|
|
|
149
|
-
| Input | Type | Default
|
|
150
|
-
| ---------------------- | ------ |
|
|
151
|
-
| `test-files-glob` | string | `
|
|
152
|
-
| `
|
|
153
|
-
| `report-slack-channel` | string | `''` | Slack channel for the results. Empty skips Slack |
|
|
153
|
+
| Input | Type | Default | Description |
|
|
154
|
+
| ---------------------- | ------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `test-files-glob` | string | `test/platform/**` | Glob of the test files to run, from the repo root. Empty means the default. See [Splitting the tests](#splitting-the-tests) |
|
|
156
|
+
| `report-slack-channel` | string | `''` | Slack channel for the results. Empty skips Slack |
|
|
154
157
|
|
|
155
158
|
Secrets: `TESTER_APIFY_TOKEN` (required), `SLACK_BOT_TOKEN`, and optionally `NPM_TOKEN`.
|
|
156
159
|
|
|
157
|
-
How often to run which tests is up to you. For example,
|
|
160
|
+
How often to run which tests is up to you, see [Splitting the tests](#splitting-the-tests). For example, the daily
|
|
161
|
+
tests once a day:
|
|
158
162
|
|
|
159
163
|
```yaml
|
|
160
164
|
name: Platform tests - Daily
|
|
@@ -169,13 +173,14 @@ jobs:
|
|
|
169
173
|
platformTestsDaily:
|
|
170
174
|
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@workflows-v0
|
|
171
175
|
with:
|
|
176
|
+
test-files-glob: 'test/platform/{daily,daily-without-pr}/**'
|
|
172
177
|
report-slack-channel: '#notif-my-repo'
|
|
173
178
|
secrets:
|
|
174
179
|
TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
|
|
175
180
|
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
|
176
181
|
```
|
|
177
182
|
|
|
178
|
-
and a few critical tests
|
|
183
|
+
and a few critical tests every hour:
|
|
179
184
|
|
|
180
185
|
```yaml
|
|
181
186
|
name: Platform tests - Hourly
|
|
@@ -190,13 +195,41 @@ jobs:
|
|
|
190
195
|
platformTestsHourly:
|
|
191
196
|
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@workflows-v0
|
|
192
197
|
with:
|
|
193
|
-
test-files-glob: hourly
|
|
198
|
+
test-files-glob: 'test/platform/{hourly,hourly-without-pr}/**'
|
|
194
199
|
report-slack-channel: '#notif-my-repo'
|
|
195
200
|
secrets:
|
|
196
201
|
TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
|
|
197
202
|
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
|
198
203
|
```
|
|
199
204
|
|
|
205
|
+
## Splitting the tests
|
|
206
|
+
|
|
207
|
+
`test-files-glob` is one glob from the repo root, for example `test/platform/**`. Both test workflows take it, and it
|
|
208
|
+
only selects which tests run. Which changed files count as test changes is a separate setting, `platformTestsGlob` in
|
|
209
|
+
the config file (default `test/platform/**`). So on a PR, a change to a `*-without-pr` test or to a shared helper
|
|
210
|
+
still builds and tests the Actors, even though the PR doesn't run that test.
|
|
211
|
+
|
|
212
|
+
Most repos split `test/platform` into folders by how often the tests run, and by whether a PR runs them:
|
|
213
|
+
|
|
214
|
+
| Folder | Runs |
|
|
215
|
+
| --------------------------------- | --------------------------------------------------------------- |
|
|
216
|
+
| `test/platform/hourly` | every hour and on every PR |
|
|
217
|
+
| `test/platform/daily` | once a day and on every PR |
|
|
218
|
+
| `test/platform/hourly-without-pr` | every hour only, e.g. tests that are too slow or costly for PRs |
|
|
219
|
+
| `test/platform/daily-without-pr` | once a day only |
|
|
220
|
+
|
|
221
|
+
Each workflow then selects its folders with a brace pattern:
|
|
222
|
+
|
|
223
|
+
| Job | `test-files-glob` |
|
|
224
|
+
| ------------------------------------- | --------------------------------------------- |
|
|
225
|
+
| PR (`public_pr-build-test.yaml`) | `test/platform/{daily,hourly}/**` |
|
|
226
|
+
| Hourly (`public_platform-tests.yaml`) | `test/platform/{hourly,hourly-without-pr}/**` |
|
|
227
|
+
| Daily (`public_platform-tests.yaml`) | `test/platform/{daily,daily-without-pr}/**` |
|
|
228
|
+
|
|
229
|
+
Write the glob to match files, not folders: `test/platform/hourly` matches no file, `test/platform/hourly/**` matches
|
|
230
|
+
every file in it. A glob that matches no file fails the test step, so a typo doesn't pass as a run with no tests.
|
|
231
|
+
`test` only runs the matched files that vitest sees as test files, so a helper in the glob runs nothing.
|
|
232
|
+
|
|
200
233
|
## `public_review.yaml`
|
|
201
234
|
|
|
202
235
|
Optional. Reviews a PR against the shared guidelines when you add the trigger label, and again on every push while
|
|
@@ -290,7 +323,7 @@ jobs:
|
|
|
290
323
|
platformTestsHourly:
|
|
291
324
|
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@workflows-v0
|
|
292
325
|
with:
|
|
293
|
-
test-files-glob: hourly
|
|
326
|
+
test-files-glob: 'test/platform/{hourly,hourly-without-pr}/**'
|
|
294
327
|
report-slack-channel: '#notif-my-repo'
|
|
295
328
|
secrets:
|
|
296
329
|
TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
|
package/package.json
CHANGED
|
@@ -165,11 +165,15 @@ describe('sandbox dummies', () => {${actorTests}
|
|
|
165
165
|
`,
|
|
166
166
|
);
|
|
167
167
|
|
|
168
|
-
const tested = await runProcess(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
const tested = await runProcess(
|
|
169
|
+
process.execPath,
|
|
170
|
+
[bundlePath, 'test', '--test-files-glob', 'test/platform/**'],
|
|
171
|
+
{
|
|
172
|
+
cwd: repoDir,
|
|
173
|
+
env: { ...cliEnv, TESTER_APIFY_TOKEN: token! },
|
|
174
|
+
input: JSON.stringify(builds),
|
|
175
|
+
},
|
|
176
|
+
);
|
|
173
177
|
expect(tested.status, tested.stderr).toBe(0);
|
|
174
178
|
expect(tested.stderr).toContain('https://console.apify.com/view/runs/');
|
|
175
179
|
|
|
@@ -36,6 +36,14 @@ describe('selectActors', () => {
|
|
|
36
36
|
]);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
it('selects an actor listed twice once, in config order', () => {
|
|
40
|
+
// build relies on this to build an Actor in both functionallyChangedActors and testChangedActors once
|
|
41
|
+
expect(names(selectActors({ actors: ['owner/c', 'owner/a', 'owner/c'], ignore: [] }, configs))).toStrictEqual([
|
|
42
|
+
'owner/a',
|
|
43
|
+
'owner/c',
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
39
47
|
it('drops the actors listed in --ignore', () => {
|
|
40
48
|
expect(names(selectActors({ actors: [], ignore: ['owner/b'] }, configs))).toStrictEqual(['owner/a', 'owner/c']);
|
|
41
49
|
});
|
|
@@ -72,10 +72,31 @@ const runGetAffectedActors = (...args: string[]) => {
|
|
|
72
72
|
return JSON.parse(result.stdout);
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
+
const names = (actors: { actorFullName: string }[]) =>
|
|
76
|
+
actors.map(({ actorFullName }) => actorFullName.replace('owner/actor-', '')).sort();
|
|
77
|
+
|
|
78
|
+
/** All three lists, for the tests about test changes */
|
|
79
|
+
const getAllChangedActors = (...args: string[]) => {
|
|
80
|
+
const document = runGetAffectedActors(...args);
|
|
81
|
+
return {
|
|
82
|
+
functional: names(document.functionallyChangedActors),
|
|
83
|
+
cosmetic: names(document.cosmeticallyChangedActors),
|
|
84
|
+
test: names(document.testChangedActors),
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/** Writes the config with actor-a's context also covering `extraContext`. It is committed with the next commit. */
|
|
89
|
+
const writeConfigWithTestsInContext = async (extraContext: string, settings: Record<string, unknown> = {}) => {
|
|
90
|
+
const actors = CONFIG.actors.map((actor) =>
|
|
91
|
+
actor.folder === 'actors/a'
|
|
92
|
+
? { ...actor, overrideActorContext: [...actor.overrideActorContext!, extraContext] }
|
|
93
|
+
: actor,
|
|
94
|
+
);
|
|
95
|
+
await writeFile(path.join(repoDir, 'apify-test-tools.config.json'), JSON.stringify({ ...settings, actors }));
|
|
96
|
+
};
|
|
97
|
+
|
|
75
98
|
const getAffectedActors = (...args: string[]) => {
|
|
76
99
|
const document = runGetAffectedActors(...args);
|
|
77
|
-
const names = (actors: { actorFullName: string }[]) =>
|
|
78
|
-
actors.map(({ actorFullName }) => actorFullName.replace('owner/actor-', '')).sort();
|
|
79
100
|
return {
|
|
80
101
|
functional: names(document.functionallyChangedActors),
|
|
81
102
|
cosmetic: names(document.cosmeticallyChangedActors),
|
|
@@ -191,8 +212,57 @@ describe('get-affected-actors CLI on real git histories', () => {
|
|
|
191
212
|
expect(getAffectedActors('--base', 'main', '--since', 'e'.repeat(40))).toStrictEqual(wholeBranch);
|
|
192
213
|
});
|
|
193
214
|
|
|
215
|
+
it('lists Actors whose platform tests changed, and a test change alone is not functional', async () => {
|
|
216
|
+
await writeConfigWithTestsInContext('test');
|
|
217
|
+
await writeFiles('test/', ['.dockerignore']);
|
|
218
|
+
// A test and its helper, both under the default platformTestsGlob test/platform/**
|
|
219
|
+
await commit('test(a): change a test and its helper', 'test/platform/a.test.ts', 'test/platform/utils.ts');
|
|
220
|
+
|
|
221
|
+
const testOnly = { functional: [], cosmetic: [], test: ['a'] };
|
|
222
|
+
// A PR runs the tests
|
|
223
|
+
expect(getAllChangedActors('--base', 'main')).toStrictEqual(testOnly);
|
|
224
|
+
// A release builds only the functional and cosmetic lists, so it builds nothing
|
|
225
|
+
expect(getAllChangedActors('--since', git('rev-parse', 'main'))).toStrictEqual(testOnly);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('lists an Actor in both lists when its source and its tests changed', async () => {
|
|
229
|
+
await writeConfigWithTestsInContext('test');
|
|
230
|
+
await commit('feat(a): change and test it', 'actors/a/main.ts', 'test/platform/a.test.ts');
|
|
231
|
+
|
|
232
|
+
expect(getAllChangedActors('--base', 'main')).toStrictEqual({ functional: ['a'], cosmetic: [], test: ['a'] });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('lists only the tests changed since the last validated commit', async () => {
|
|
236
|
+
await writeConfigWithTestsInContext('test');
|
|
237
|
+
const validated = await commit('feat(a): validated change', 'actors/a/main.ts');
|
|
238
|
+
await commit('test(a): new test', 'test/platform/a.test.ts');
|
|
239
|
+
|
|
240
|
+
// The validated build of A has the branch's code, only the tests changed since
|
|
241
|
+
expect(getAllChangedActors('--base', 'main', '--since', validated)).toStrictEqual({
|
|
242
|
+
functional: [],
|
|
243
|
+
cosmetic: [],
|
|
244
|
+
test: ['a'],
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('reads which files are platform tests from platformTestsGlob in the config', async () => {
|
|
249
|
+
await writeConfigWithTestsInContext('e2e', { platformTestsGlob: 'e2e/platform/**' });
|
|
250
|
+
await commit('test(a): new test', 'e2e/platform/a.test.ts');
|
|
251
|
+
expect(getAllChangedActors('--base', 'main')).toStrictEqual({ functional: [], cosmetic: [], test: ['a'] });
|
|
252
|
+
|
|
253
|
+
// Outside the glob, a file in the context is a source change like any other
|
|
254
|
+
await commit('feat(a): other file', 'e2e/fixtures/data.json');
|
|
255
|
+
expect(getAllChangedActors('--base', 'main')).toStrictEqual({ functional: ['a'], cosmetic: [], test: ['a'] });
|
|
256
|
+
});
|
|
257
|
+
|
|
194
258
|
it('reports nothing for an empty range', async () => {
|
|
195
|
-
const nothing = {
|
|
259
|
+
const nothing = {
|
|
260
|
+
commits: [],
|
|
261
|
+
changedFiles: [],
|
|
262
|
+
functionallyChangedActors: [],
|
|
263
|
+
cosmeticallyChangedActors: [],
|
|
264
|
+
testChangedActors: [],
|
|
265
|
+
};
|
|
196
266
|
// The branch has no commits of its own yet
|
|
197
267
|
expect(runGetAffectedActors('--base', 'main')).toMatchObject(nothing);
|
|
198
268
|
// A release with nothing new since the last one
|
|
@@ -24,9 +24,10 @@ describe('getAffectedActors', () => {
|
|
|
24
24
|
|
|
25
25
|
beforeEach(() => {
|
|
26
26
|
vi.spyOn(LoadConfig, 'readConfigFile').mockResolvedValue([actorA, actorB]);
|
|
27
|
+
vi.spyOn(LoadConfig, 'readRepoSettings').mockResolvedValue({ platformTestsGlob: 'test/platform/**' });
|
|
27
28
|
changedActorsSpy = vi
|
|
28
29
|
.spyOn(DiffChanges, 'getChangedActors')
|
|
29
|
-
.mockResolvedValue({ functional: [actorA], cosmetic: [actorB] });
|
|
30
|
+
.mockResolvedValue({ functional: [actorA], cosmetic: [actorB], test: [] });
|
|
30
31
|
vi.spyOn(Git, 'resolveCommitSha').mockResolvedValue(headSha);
|
|
31
32
|
vi.spyOn(Git, 'getChangedFilesBetween').mockResolvedValue(changedFiles);
|
|
32
33
|
vi.spyOn(Git, 'getChangelogAdditions').mockResolvedValue('- Added foo');
|
|
@@ -62,6 +63,7 @@ describe('getAffectedActors', () => {
|
|
|
62
63
|
// Only the names, the config file stays the source of truth for the rest
|
|
63
64
|
functionallyChangedActors: [{ actorFullName: 'owner/a' }],
|
|
64
65
|
cosmeticallyChangedActors: [{ actorFullName: 'owner/b' }],
|
|
66
|
+
testChangedActors: [],
|
|
65
67
|
});
|
|
66
68
|
expect(Git.getMergeBase).toHaveBeenCalledWith('origin/master', 'HEAD');
|
|
67
69
|
expect(Git.getChangedFilesBetween).toHaveBeenCalledWith(mergeBaseSha, headSha);
|
|
@@ -71,6 +73,7 @@ describe('getAffectedActors', () => {
|
|
|
71
73
|
filepathsChanged: changedFiles,
|
|
72
74
|
actorConfigs: [actorA, actorB],
|
|
73
75
|
range: { fromRef: mergeBaseSha, toRef: headSha },
|
|
76
|
+
platformTestsGlob: 'test/platform/**',
|
|
74
77
|
});
|
|
75
78
|
expect(findCommitSpy).not.toHaveBeenCalled();
|
|
76
79
|
});
|
|
@@ -96,15 +99,20 @@ describe('getAffectedActors', () => {
|
|
|
96
99
|
return {
|
|
97
100
|
functional: result.functionallyChangedActors.map(({ actorFullName }) => actorFullName),
|
|
98
101
|
cosmetic: result.cosmeticallyChangedActors.map(({ actorFullName }) => actorFullName),
|
|
102
|
+
test: result.testChangedActors.map(({ actorFullName }) => actorFullName),
|
|
99
103
|
};
|
|
100
104
|
};
|
|
101
105
|
|
|
102
106
|
it('classifies the changes since --since separately', async () => {
|
|
103
|
-
await getNarrowed(
|
|
107
|
+
await getNarrowed(
|
|
108
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
109
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
110
|
+
);
|
|
104
111
|
expect(changedActorsSpy).toHaveBeenCalledWith({
|
|
105
112
|
filepathsChanged: sinceFiles,
|
|
106
113
|
actorConfigs: [actorA, actorB],
|
|
107
114
|
range: { fromRef: sinceSha, toRef: headSha },
|
|
115
|
+
platformTestsGlob: 'test/platform/**',
|
|
108
116
|
});
|
|
109
117
|
});
|
|
110
118
|
|
|
@@ -116,25 +124,55 @@ describe('getAffectedActors', () => {
|
|
|
116
124
|
|
|
117
125
|
it('retests an Actor whose validated change meets a new functional change', async () => {
|
|
118
126
|
await expect(
|
|
119
|
-
getNarrowed(
|
|
120
|
-
|
|
127
|
+
getNarrowed(
|
|
128
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
129
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
130
|
+
),
|
|
131
|
+
).resolves.toStrictEqual({ functional: ['owner/a'], cosmetic: [], test: [] });
|
|
121
132
|
});
|
|
122
133
|
|
|
123
134
|
it('leaves out Actors that only the branch changed before --since or only --base changed', async () => {
|
|
124
135
|
await expect(
|
|
125
|
-
getNarrowed(
|
|
126
|
-
|
|
136
|
+
getNarrowed(
|
|
137
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
138
|
+
{ functional: [actorB], cosmetic: [], test: [] },
|
|
139
|
+
),
|
|
140
|
+
).resolves.toStrictEqual({ functional: [], cosmetic: [], test: [] });
|
|
127
141
|
});
|
|
128
142
|
|
|
129
143
|
it('takes the less impactful change when the two disagree', async () => {
|
|
130
144
|
// A validated functional change, then only cosmetic changes (e.g. a README merged in from base)
|
|
131
145
|
await expect(
|
|
132
|
-
getNarrowed(
|
|
133
|
-
|
|
146
|
+
getNarrowed(
|
|
147
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
148
|
+
{ functional: [], cosmetic: [actorA], test: [] },
|
|
149
|
+
),
|
|
150
|
+
).resolves.toStrictEqual({ functional: [], cosmetic: ['owner/a'], test: [] });
|
|
134
151
|
// A functional change merged in from base, while the branch only changes the README
|
|
135
152
|
await expect(
|
|
136
|
-
getNarrowed(
|
|
137
|
-
|
|
153
|
+
getNarrowed(
|
|
154
|
+
{ functional: [], cosmetic: [actorA], test: [] },
|
|
155
|
+
{ functional: [actorA], cosmetic: [], test: [] },
|
|
156
|
+
),
|
|
157
|
+
).resolves.toStrictEqual({ functional: [], cosmetic: ['owner/a'], test: [] });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('runs the tests of an Actor whose tests the branch changed and changed again since --since', async () => {
|
|
161
|
+
await expect(
|
|
162
|
+
getNarrowed(
|
|
163
|
+
{ functional: [actorA], cosmetic: [], test: [actorA, actorB] },
|
|
164
|
+
{ functional: [], cosmetic: [], test: [actorA] },
|
|
165
|
+
),
|
|
166
|
+
).resolves.toStrictEqual({ functional: [], cosmetic: [], test: ['owner/a'] });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('leaves out test changes that only --base or only the validated part of the branch has', async () => {
|
|
170
|
+
await expect(
|
|
171
|
+
getNarrowed(
|
|
172
|
+
{ functional: [], cosmetic: [], test: [actorA] },
|
|
173
|
+
{ functional: [], cosmetic: [], test: [actorB] },
|
|
174
|
+
),
|
|
175
|
+
).resolves.toStrictEqual({ functional: [], cosmetic: [], test: [] });
|
|
138
176
|
});
|
|
139
177
|
|
|
140
178
|
it.each([
|
|
@@ -172,6 +210,7 @@ describe('getAffectedActors', () => {
|
|
|
172
210
|
// Only the names, the config file stays the source of truth for the rest
|
|
173
211
|
functionallyChangedActors: [{ actorFullName: 'owner/a' }],
|
|
174
212
|
cosmeticallyChangedActors: [{ actorFullName: 'owner/b' }],
|
|
213
|
+
testChangedActors: [],
|
|
175
214
|
});
|
|
176
215
|
expect(resolveBaseSpy).toHaveBeenCalledWith('HEAD~2');
|
|
177
216
|
expect(commitsBetweenSpy).toHaveBeenCalledWith(sinceSha, headSha);
|