apify-test-tools 0.10.2-beta.1 → 0.10.2-beta.11

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 (189) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/actionlint.yaml +11 -0
  3. package/.github/actions/checkout-restore-dependencies/action.yaml +64 -28
  4. package/.github/cli/apify-test-tools.mjs +360 -0
  5. package/.github/scripts/mask-config-secrets.sh +17 -0
  6. package/.github/scripts/run-with-apify-tokens.mjs +37 -12
  7. package/.github/workflows/_check_code.yaml +25 -16
  8. package/.github/workflows/_move_major_tag.yaml +5 -83
  9. package/.github/workflows/_tests.yaml +34 -0
  10. package/.github/workflows/_update_release_metadata.yaml +0 -12
  11. package/.github/workflows/manual_move_major_tag.yaml +3 -3
  12. package/.github/workflows/manual_release_stable.yaml +3 -13
  13. package/.github/workflows/on_master.yaml +9 -5
  14. package/.github/workflows/on_pull_request.yaml +3 -48
  15. package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +16 -3
  16. package/.github/workflows/public_platform-tests.yaml +27 -3
  17. package/.github/workflows/public_pr-build-test.yaml +67 -29
  18. package/.github/workflows/public_push-build-latest.yaml +44 -10
  19. package/.github/workflows/public_review.yaml +5 -4
  20. package/.husky/pre-commit +5 -0
  21. package/.prettierignore +3 -0
  22. package/CHANGELOG.md +17 -0
  23. package/CONTRIBUTING.md +112 -24
  24. package/README.md +218 -600
  25. package/bin/affected-actors.ts +198 -0
  26. package/bin/build-from-local.ts +65 -13
  27. package/bin/build.ts +123 -41
  28. package/bin/diff-changes.ts +32 -23
  29. package/bin/diff-json-schema.ts +5 -6
  30. package/bin/git.ts +69 -99
  31. package/bin/main.ts +96 -135
  32. package/bin/slack.ts +43 -10
  33. package/bin/stdin-input.ts +65 -0
  34. package/bin/test-report.ts +6 -9
  35. package/bin/types.ts +15 -0
  36. package/dist/bin/affected-actors.d.ts +46 -0
  37. package/dist/bin/affected-actors.d.ts.map +1 -0
  38. package/dist/bin/affected-actors.js +135 -0
  39. package/dist/bin/affected-actors.js.map +1 -0
  40. package/dist/bin/build-from-local.d.ts +12 -3
  41. package/dist/bin/build-from-local.d.ts.map +1 -1
  42. package/dist/bin/build-from-local.js +34 -12
  43. package/dist/bin/build-from-local.js.map +1 -1
  44. package/dist/bin/build.d.ts +30 -7
  45. package/dist/bin/build.d.ts.map +1 -1
  46. package/dist/bin/build.js +85 -26
  47. package/dist/bin/build.js.map +1 -1
  48. package/dist/bin/diff-changes.d.ts +11 -4
  49. package/dist/bin/diff-changes.d.ts.map +1 -1
  50. package/dist/bin/diff-changes.js +19 -19
  51. package/dist/bin/diff-changes.js.map +1 -1
  52. package/dist/bin/diff-json-schema.d.ts +2 -2
  53. package/dist/bin/diff-json-schema.d.ts.map +1 -1
  54. package/dist/bin/diff-json-schema.js +1 -4
  55. package/dist/bin/diff-json-schema.js.map +1 -1
  56. package/dist/bin/git.d.ts +23 -21
  57. package/dist/bin/git.d.ts.map +1 -1
  58. package/dist/bin/git.js +60 -83
  59. package/dist/bin/git.js.map +1 -1
  60. package/dist/bin/main.js +82 -105
  61. package/dist/bin/main.js.map +1 -1
  62. package/dist/bin/slack.d.ts +14 -4
  63. package/dist/bin/slack.d.ts.map +1 -1
  64. package/dist/bin/slack.js +39 -6
  65. package/dist/bin/slack.js.map +1 -1
  66. package/dist/bin/stdin-input.d.ts +17 -0
  67. package/dist/bin/stdin-input.d.ts.map +1 -0
  68. package/dist/bin/stdin-input.js +54 -0
  69. package/dist/bin/stdin-input.js.map +1 -0
  70. package/dist/bin/test-report.d.ts.map +1 -1
  71. package/dist/bin/test-report.js +6 -8
  72. package/dist/bin/test-report.js.map +1 -1
  73. package/dist/bin/types.d.ts +13 -0
  74. package/dist/bin/types.d.ts.map +1 -1
  75. package/dist/lib/extend-expect.d.ts.map +1 -1
  76. package/dist/lib/extend-expect.js +37 -218
  77. package/dist/lib/extend-expect.js.map +1 -1
  78. package/dist/lib/lib.d.ts +1 -2
  79. package/dist/lib/lib.d.ts.map +1 -1
  80. package/dist/lib/lib.js +15 -22
  81. package/dist/lib/lib.js.map +1 -1
  82. package/dist/lib/run-checks.d.ts +14 -0
  83. package/dist/lib/run-checks.d.ts.map +1 -0
  84. package/dist/lib/run-checks.js +82 -0
  85. package/dist/lib/run-checks.js.map +1 -0
  86. package/dist/lib/run-test-result.d.ts +3 -6
  87. package/dist/lib/run-test-result.d.ts.map +1 -1
  88. package/dist/lib/run-test-result.js +41 -54
  89. package/dist/lib/run-test-result.js.map +1 -1
  90. package/dist/test/bundle/cli-bundle.test.d.ts +2 -0
  91. package/dist/test/bundle/cli-bundle.test.d.ts.map +1 -0
  92. package/dist/test/bundle/cli-bundle.test.js +190 -0
  93. package/dist/test/bundle/cli-bundle.test.js.map +1 -0
  94. package/dist/test/e2e/cli-bundle.e2e.test.d.ts +2 -0
  95. package/dist/test/e2e/cli-bundle.e2e.test.d.ts.map +1 -0
  96. package/dist/test/e2e/cli-bundle.e2e.test.js +138 -0
  97. package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -0
  98. package/dist/test/e2e/git-build-release.e2e.test.d.ts +2 -0
  99. package/dist/test/e2e/git-build-release.e2e.test.d.ts.map +1 -0
  100. package/dist/test/e2e/git-build-release.e2e.test.js +87 -0
  101. package/dist/test/e2e/git-build-release.e2e.test.js.map +1 -0
  102. package/dist/test/e2e/helpers.d.ts +37 -0
  103. package/dist/test/e2e/helpers.d.ts.map +1 -0
  104. package/dist/test/e2e/helpers.js +57 -0
  105. package/dist/test/e2e/helpers.js.map +1 -0
  106. package/dist/test/unit/bin/affected-actors-cli.test.d.ts +2 -0
  107. package/dist/test/unit/bin/affected-actors-cli.test.d.ts.map +1 -0
  108. package/dist/test/unit/bin/affected-actors-cli.test.js +167 -0
  109. package/dist/test/unit/bin/affected-actors-cli.test.js.map +1 -0
  110. package/dist/test/unit/bin/affected-actors.test.d.ts +2 -0
  111. package/dist/test/unit/bin/affected-actors.test.d.ts.map +1 -0
  112. package/dist/test/unit/bin/affected-actors.test.js +141 -0
  113. package/dist/test/unit/bin/affected-actors.test.js.map +1 -0
  114. package/dist/test/unit/bin/build-cli.test.d.ts +2 -0
  115. package/dist/test/unit/bin/build-cli.test.d.ts.map +1 -0
  116. package/dist/test/unit/bin/build-cli.test.js +44 -0
  117. package/dist/test/unit/bin/build-cli.test.js.map +1 -0
  118. package/dist/test/unit/bin/build-from-local.test.js +65 -1
  119. package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
  120. package/dist/test/unit/bin/build.test.js +223 -3
  121. package/dist/test/unit/bin/build.test.js.map +1 -1
  122. package/dist/test/unit/bin/compiled-cli.d.ts +6 -0
  123. package/dist/test/unit/bin/compiled-cli.d.ts.map +1 -0
  124. package/dist/test/unit/bin/compiled-cli.js +16 -0
  125. package/dist/test/unit/bin/compiled-cli.js.map +1 -0
  126. package/dist/test/unit/bin/diff-changes.test.js +130 -101
  127. package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
  128. package/dist/test/unit/bin/diff.test.js +25 -28
  129. package/dist/test/unit/bin/diff.test.js.map +1 -1
  130. package/dist/test/unit/bin/git.test.js +69 -152
  131. package/dist/test/unit/bin/git.test.js.map +1 -1
  132. package/dist/test/unit/bin/slack.test.d.ts +2 -0
  133. package/dist/test/unit/bin/slack.test.d.ts.map +1 -0
  134. package/dist/test/unit/bin/slack.test.js +71 -0
  135. package/dist/test/unit/bin/slack.test.js.map +1 -0
  136. package/dist/test/unit/bin/stdin-input.test.d.ts +2 -0
  137. package/dist/test/unit/bin/stdin-input.test.d.ts.map +1 -0
  138. package/dist/test/unit/bin/stdin-input.test.js +50 -0
  139. package/dist/test/unit/bin/stdin-input.test.js.map +1 -0
  140. package/dist/test/unit/github-scripts/mask-config-secrets.test.d.ts +2 -0
  141. package/dist/test/unit/github-scripts/mask-config-secrets.test.d.ts.map +1 -0
  142. package/dist/test/unit/github-scripts/mask-config-secrets.test.js +50 -0
  143. package/dist/test/unit/github-scripts/mask-config-secrets.test.js.map +1 -0
  144. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.d.ts +2 -0
  145. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.d.ts.map +1 -0
  146. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.js +102 -0
  147. package/dist/test/unit/github-scripts/run-with-apify-tokens.test.js.map +1 -0
  148. package/dist/test/unit/run-checks.test.d.ts +2 -0
  149. package/dist/test/unit/run-checks.test.d.ts.map +1 -0
  150. package/dist/test/unit/run-checks.test.js +62 -0
  151. package/dist/test/unit/run-checks.test.js.map +1 -0
  152. package/dist/test/unit/run-test-result.test.d.ts +2 -0
  153. package/dist/test/unit/run-test-result.test.d.ts.map +1 -0
  154. package/dist/test/unit/run-test-result.test.js +34 -0
  155. package/dist/test/unit/run-test-result.test.js.map +1 -0
  156. package/dist/test/unit/should-built-and-test.test.js +41 -38
  157. package/dist/test/unit/should-built-and-test.test.js.map +1 -1
  158. package/dist/tsconfig.tsbuildinfo +1 -1
  159. package/docs/cli.md +373 -0
  160. package/docs/lib.md +271 -0
  161. package/docs/workflows.md +370 -0
  162. package/lib/extend-expect.ts +48 -258
  163. package/lib/lib.ts +24 -28
  164. package/lib/run-checks.ts +101 -0
  165. package/lib/run-test-result.ts +45 -55
  166. package/package.json +13 -3
  167. package/test/bundle/cli-bundle.test.ts +235 -0
  168. package/test/e2e/cli-bundle.e2e.test.ts +207 -0
  169. package/test/e2e/git-build-release.e2e.test.ts +113 -0
  170. package/test/e2e/helpers.ts +82 -0
  171. package/test/unit/bin/affected-actors-cli.test.ts +201 -0
  172. package/test/unit/bin/affected-actors.test.ts +182 -0
  173. package/test/unit/bin/build-cli.test.ts +55 -0
  174. package/test/unit/bin/build-from-local.test.ts +71 -0
  175. package/test/unit/bin/build.test.ts +279 -3
  176. package/test/unit/bin/compiled-cli.ts +17 -0
  177. package/test/unit/bin/diff-changes.test.ts +139 -101
  178. package/test/unit/bin/diff.test.ts +25 -28
  179. package/test/unit/bin/git.test.ts +85 -176
  180. package/test/unit/bin/slack.test.ts +84 -0
  181. package/test/unit/bin/stdin-input.test.ts +61 -0
  182. package/test/unit/github-scripts/mask-config-secrets.test.ts +61 -0
  183. package/test/unit/github-scripts/run-with-apify-tokens.test.ts +115 -0
  184. package/test/unit/run-checks.test.ts +76 -0
  185. package/test/unit/run-test-result.test.ts +44 -0
  186. package/test/unit/should-built-and-test.test.ts +46 -39
  187. package/.github/scripts/check-major-tag-refs.mjs +0 -72
  188. package/.github/scripts/check-package-version-bump.mjs +0 -74
  189. package/.github/workflows-package-version +0 -1
@@ -59,7 +59,17 @@ const amazonActor = actorConfig('junglee/amazon-crawler', 'actors/junglee_Amazon
59
59
  'shared',
60
60
  ]);
61
61
 
62
- const commits = [{ sha: 'Commit1', author: '', date: '', message: '' }];
62
+ const range = { fromRef: 'Commit0', toRef: 'Commit1' };
63
+
64
+ // What a PR (functional changes only) or a release (isLatest, all changes) builds, in config order
65
+ const changedActorsFor = async ({
66
+ isLatest = false,
67
+ ...options
68
+ }: Parameters<typeof getChangedActors>[0] & { isLatest?: boolean }): Promise<ActorConfig[]> => {
69
+ const { functional, cosmetic } = await getChangedActors(options);
70
+ const changed = isLatest ? [...functional, ...cosmetic] : functional;
71
+ return options.actorConfigs.filter((config) => changed.includes(config));
72
+ };
63
73
 
64
74
  describe('getChangedActors', () => {
65
75
  beforeEach(() => {
@@ -68,51 +78,51 @@ describe('getChangedActors', () => {
68
78
  });
69
79
 
70
80
  it('returns empty array when no files changed', async () => {
71
- expect(await getChangedActors({ filepathsChanged: [], actorConfigs, commits })).toEqual([]);
81
+ expect(await changedActorsFor({ filepathsChanged: [], actorConfigs, range })).toEqual([]);
72
82
  });
73
83
 
74
84
  it('returns empty array when only ignored top-level files changed', async () => {
75
- const result = await getChangedActors({
85
+ const result = await changedActorsFor({
76
86
  filepathsChanged: ['.gitignore', 'README.md', '.husky/pre-commit', '.vscode/settings.json'],
77
87
  actorConfigs,
78
- commits,
88
+ range,
79
89
  });
80
90
  expect(result).toEqual([]);
81
91
  });
82
92
 
83
93
  it('does not ignore source files whose names contain an ignored file name', async () => {
84
- const result = await getChangedActors({
94
+ const result = await changedActorsFor({
85
95
  filepathsChanged: ['actors/foo_bar/src/.gitignore-helper.ts'],
86
96
  actorConfigs,
87
- commits,
97
+ range,
88
98
  });
89
99
  expect(result).toEqual([miniActor]);
90
100
  });
91
101
 
92
102
  it('returns the actor when a functional file in its folder changes', async () => {
93
- const result = await getChangedActors({
103
+ const result = await changedActorsFor({
94
104
  filepathsChanged: ['actors/foo_bar/src/main.ts'],
95
105
  actorConfigs,
96
- commits,
106
+ range,
97
107
  });
98
108
  expect(result).toEqual([miniActor]);
99
109
  });
100
110
 
101
111
  it('returns actor when isLatest and README in actor folder changed (cosmetic)', async () => {
102
- const result = await getChangedActors({
112
+ const result = await changedActorsFor({
103
113
  filepathsChanged: ['actors/foo_bar/README.md'],
104
114
  actorConfigs,
105
- commits,
115
+ range,
106
116
  isLatest: true,
107
117
  });
108
118
  expect(result).toEqual([miniActor]);
109
119
  });
110
120
 
111
121
  it('does not return actor when not isLatest and only README changed (cosmetic)', async () => {
112
- const result = await getChangedActors({
122
+ const result = await changedActorsFor({
113
123
  filepathsChanged: ['actors/foo_bar/README.md'],
114
124
  actorConfigs,
115
- commits,
125
+ range,
116
126
  isLatest: false,
117
127
  });
118
128
  expect(result).toEqual([]);
@@ -120,10 +130,10 @@ describe('getChangedActors', () => {
120
130
 
121
131
  it('returns actor when isLatest and JSON file has only cosmetic changes', async () => {
122
132
  vi.spyOn(DiffJsonSchema, 'isCosmeticOnlyJsonSchemaChange').mockResolvedValue(true);
123
- const result = await getChangedActors({
133
+ const result = await changedActorsFor({
124
134
  filepathsChanged: ['actors/foo_bar/.actor/actor.json'],
125
135
  actorConfigs,
126
- commits,
136
+ range,
127
137
  isLatest: true,
128
138
  });
129
139
  expect(result).toEqual([miniActor]);
@@ -131,30 +141,30 @@ describe('getChangedActors', () => {
131
141
 
132
142
  it('does not return actor when not isLatest and JSON file has only cosmetic changes', async () => {
133
143
  vi.spyOn(DiffJsonSchema, 'isCosmeticOnlyJsonSchemaChange').mockResolvedValue(true);
134
- const result = await getChangedActors({
144
+ const result = await changedActorsFor({
135
145
  filepathsChanged: ['actors/foo_bar/.actor/actor.json'],
136
146
  actorConfigs,
137
- commits,
147
+ range,
138
148
  isLatest: false,
139
149
  });
140
150
  expect(result).toEqual([]);
141
151
  });
142
152
 
143
153
  it('returns actor when JSON file has functional changes', async () => {
144
- const result = await getChangedActors({
154
+ const result = await changedActorsFor({
145
155
  filepathsChanged: ['actors/foo_bar/.actor/actor.json'],
146
156
  actorConfigs,
147
- commits,
157
+ range,
148
158
  });
149
159
  expect(result).toEqual([miniActor]);
150
160
  });
151
161
 
152
162
  it('JSON file in actor folder but outside .actor/ is functional, not checked for cosmetic', async () => {
153
163
  vi.spyOn(DiffJsonSchema, 'isCosmeticOnlyJsonSchemaChange').mockResolvedValue(true);
154
- const result = await getChangedActors({
164
+ const result = await changedActorsFor({
155
165
  filepathsChanged: ['actors/foo_bar/package.json'],
156
166
  actorConfigs,
157
- commits,
167
+ range,
158
168
  isLatest: false,
159
169
  });
160
170
  expect(result).toEqual([miniActor]);
@@ -163,34 +173,34 @@ describe('getChangedActors', () => {
163
173
 
164
174
  it('JSON file under .actor/ inside actor folder is checked for cosmetic changes', async () => {
165
175
  vi.spyOn(DiffJsonSchema, 'isCosmeticOnlyJsonSchemaChange').mockResolvedValue(true);
166
- const result = await getChangedActors({
176
+ const result = await changedActorsFor({
167
177
  filepathsChanged: ['actors/foo_bar/.actor/input_schema.json'],
168
178
  actorConfigs,
169
- commits,
179
+ range,
170
180
  isLatest: false,
171
181
  });
172
182
  expect(result).toEqual([]);
173
183
  expect(DiffJsonSchema.isCosmeticOnlyJsonSchemaChange).toHaveBeenCalledWith(
174
- commits,
184
+ range,
175
185
  'actors/foo_bar/.actor/input_schema.json',
176
186
  );
177
187
  });
178
188
 
179
189
  it('does not trigger narrow-context actor when shared file changes', async () => {
180
- const result = await getChangedActors({
190
+ const result = await changedActorsFor({
181
191
  filepathsChanged: ['shared/utils.ts'],
182
192
  actorConfigs,
183
- commits,
193
+ range,
184
194
  });
185
195
  expect(result).toContainEqual(miniActor);
186
196
  expect(result).not.toContainEqual(standaloneActor);
187
197
  });
188
198
 
189
199
  it('root-level changelog outside any actor folder is cosmetic for every actor when isLatest', async () => {
190
- const result = await getChangedActors({
200
+ const result = await changedActorsFor({
191
201
  filepathsChanged: ['CHANGELOG.md'],
192
202
  actorConfigs,
193
- commits,
203
+ range,
194
204
  isLatest: true,
195
205
  });
196
206
  expect(result).toEqual(expect.arrayContaining([miniActor, standaloneActor]));
@@ -198,20 +208,20 @@ describe('getChangedActors', () => {
198
208
  });
199
209
 
200
210
  it('root-level changelog is not cosmetic-triggered when not isLatest', async () => {
201
- const result = await getChangedActors({
211
+ const result = await changedActorsFor({
202
212
  filepathsChanged: ['CHANGELOG.md'],
203
213
  actorConfigs,
204
- commits,
214
+ range,
205
215
  isLatest: false,
206
216
  });
207
217
  expect(result).toEqual([]);
208
218
  });
209
219
 
210
220
  it('treats any changelog filename as cosmetic regardless of configured paths', async () => {
211
- const result = await getChangedActors({
221
+ const result = await changedActorsFor({
212
222
  filepathsChanged: ['docs/CHANGELOG.md'],
213
223
  actorConfigs: [miniActor, standaloneActor],
214
- commits,
224
+ range,
215
225
  isLatest: true,
216
226
  });
217
227
  expect(result).toEqual([miniActor, standaloneActor]);
@@ -222,39 +232,39 @@ describe('getChangedActors', () => {
222
232
  ...standaloneActor,
223
233
  actorJson: { ...standaloneActor.actorJson, readme: new RelativeFile('docs/guide.md') },
224
234
  };
225
- const result = await getChangedActors({
235
+ const result = await changedActorsFor({
226
236
  filepathsChanged: ['docs/guide.md'],
227
237
  actorConfigs: [configuredActor],
228
- commits,
238
+ range,
229
239
  isLatest: true,
230
240
  });
231
241
  expect(result).toEqual([]);
232
242
  });
233
243
 
234
244
  it('triggers narrow-context actor when its own folder changes', async () => {
235
- const result = await getChangedActors({
245
+ const result = await changedActorsFor({
236
246
  filepathsChanged: ['standalone-actors/standalone/src/main.ts'],
237
247
  actorConfigs,
238
- commits,
248
+ range,
239
249
  });
240
250
  expect(result).toContainEqual(standaloneActor);
241
251
  });
242
252
 
243
253
  it('deduplicates actors when multiple files in same actor folder change', async () => {
244
- const result = await getChangedActors({
254
+ const result = await changedActorsFor({
245
255
  filepathsChanged: ['actors/foo_bar/src/main.ts', 'actors/foo_bar/package.json'],
246
256
  actorConfigs,
247
- commits,
257
+ range,
248
258
  });
249
259
  expect(result).toHaveLength(1);
250
260
  expect(result).toContainEqual(miniActor);
251
261
  });
252
262
 
253
263
  it('handles mixed changes: returns both broad and narrow-context actors', async () => {
254
- const result = await getChangedActors({
264
+ const result = await changedActorsFor({
255
265
  filepathsChanged: ['actors/foo_bar/src/main.ts', 'standalone-actors/standalone/Dockerfile'],
256
266
  actorConfigs,
257
- commits,
267
+ range,
258
268
  });
259
269
  expect(result).toContainEqual(miniActor);
260
270
  expect(result).toContainEqual(standaloneActor);
@@ -262,29 +272,29 @@ describe('getChangedActors', () => {
262
272
 
263
273
  it('matches folder where folder name differs from actor name', async () => {
264
274
  const ownerlessActor = actorConfig('myteam/shopify-scraper', 'actors/shopify', 'APIFY_TOKEN_MYTEAM', '', ['']);
265
- const result = await getChangedActors({
275
+ const result = await changedActorsFor({
266
276
  filepathsChanged: ['actors/shopify/src/main.ts'],
267
277
  actorConfigs: [ownerlessActor],
268
- commits,
278
+ range,
269
279
  });
270
280
  expect(result).toEqual([ownerlessActor]);
271
281
  });
272
282
 
273
283
  it('in single-actor repo, .actor/ changes trigger builds', async () => {
274
284
  const rootActor = actorConfig('myteam/my-actor', '', 'BUILDER_APIFY_TOKEN', '', ['']);
275
- const result = await getChangedActors({
285
+ const result = await changedActorsFor({
276
286
  filepathsChanged: ['.actor/actor.json'],
277
287
  actorConfigs: [rootActor],
278
- commits,
288
+ range,
279
289
  });
280
290
  expect(result).toEqual([rootActor]);
281
291
  });
282
292
 
283
293
  it('in multi-actor repo, .actor/ changes only trigger broad-context actors', async () => {
284
- const result = await getChangedActors({
294
+ const result = await changedActorsFor({
285
295
  filepathsChanged: ['.actor/actor.json'],
286
296
  actorConfigs,
287
- commits,
297
+ range,
288
298
  });
289
299
  expect(result).toEqual([miniActor]);
290
300
  });
@@ -293,10 +303,10 @@ describe('getChangedActors', () => {
293
303
  const narrowActor = actorConfig('foo/bar', 'actors/foo_bar', 'APIFY_TOKEN_FOO', 'actors/foo_bar', [
294
304
  'actors/foo_bar',
295
305
  ]);
296
- const result = await getChangedActors({
306
+ const result = await changedActorsFor({
297
307
  filepathsChanged: ['Actors/FOO_BAR/Main.ts'],
298
308
  actorConfigs: [narrowActor],
299
- commits,
309
+ range,
300
310
  });
301
311
  expect(result).toEqual([]);
302
312
  });
@@ -309,10 +319,10 @@ describe('getChangedActors', () => {
309
319
  'actors/override',
310
320
  ['actors/override', 'packages'],
311
321
  );
312
- const result = await getChangedActors({
322
+ const result = await changedActorsFor({
313
323
  filepathsChanged: ['packages/shared/utils.ts'],
314
324
  actorConfigs: [overrideActor],
315
- commits,
325
+ range,
316
326
  });
317
327
  expect(result).toEqual([overrideActor]);
318
328
  });
@@ -325,10 +335,10 @@ describe('getChangedActors', () => {
325
335
  'actors/override',
326
336
  ['actors/override', 'packages'],
327
337
  );
328
- const result = await getChangedActors({
338
+ const result = await changedActorsFor({
329
339
  filepathsChanged: ['other-dir/file.ts'],
330
340
  actorConfigs: [overrideActor],
331
- commits,
341
+ range,
332
342
  });
333
343
  expect(result).toEqual([]);
334
344
  });
@@ -336,10 +346,10 @@ describe('getChangedActors', () => {
336
346
  it('broad-context actor skips files in sibling actor folders', async () => {
337
347
  const actorA = actorConfig('team/actor-a', 'actors/a', 'APIFY_TOKEN_TEAM', '', ['']);
338
348
  const actorB = actorConfig('team/actor-b', 'actors/b', 'APIFY_TOKEN_TEAM', '', ['']);
339
- const result = await getChangedActors({
349
+ const result = await changedActorsFor({
340
350
  filepathsChanged: ['actors/b/src/main.ts'],
341
351
  actorConfigs: [actorA, actorB],
342
- commits,
352
+ range,
343
353
  });
344
354
  expect(result).toEqual([actorB]);
345
355
  });
@@ -349,10 +359,10 @@ describe('getChangedActors', () => {
349
359
  const childActor = actorConfig('team/child', 'actors/child', 'APIFY_TOKEN_TEAM', 'actors/child', [
350
360
  'actors/child',
351
361
  ]);
352
- const result = await getChangedActors({
362
+ const result = await changedActorsFor({
353
363
  filepathsChanged: ['actors/child/src/main.ts'],
354
364
  actorConfigs: [rootActor, childActor],
355
- commits,
365
+ range,
356
366
  });
357
367
  expect(result).not.toContainEqual(rootActor);
358
368
  expect(result).toContainEqual(childActor);
@@ -363,10 +373,10 @@ describe('getChangedActors', () => {
363
373
  const childActor = actorConfig('team/child', 'actors/child', 'APIFY_TOKEN_TEAM', 'actors/child', [
364
374
  'actors/child',
365
375
  ]);
366
- const result = await getChangedActors({
376
+ const result = await changedActorsFor({
367
377
  filepathsChanged: ['lib/shared-utils.ts'],
368
378
  actorConfigs: [rootActor, childActor],
369
- commits,
379
+ range,
370
380
  });
371
381
  expect(result).toContainEqual(rootActor);
372
382
  expect(result).not.toContainEqual(childActor);
@@ -376,30 +386,30 @@ describe('getChangedActors', () => {
376
386
  vi.spyOn(Dockerignore, 'loadDockerIgnore').mockReturnValue(
377
387
  (filePath) => filePath === 'actors/foo_bar/node_modules/foo.js',
378
388
  );
379
- const result = await getChangedActors({
389
+ const result = await changedActorsFor({
380
390
  filepathsChanged: ['actors/foo_bar/node_modules/foo.js'],
381
391
  actorConfigs: [miniActor],
382
- commits,
392
+ range,
383
393
  });
384
394
  expect(result).toEqual([]);
385
395
  });
386
396
 
387
397
  it('file not matched by .dockerignore is classified normally', async () => {
388
398
  vi.spyOn(Dockerignore, 'loadDockerIgnore').mockReturnValue((filePath) => filePath.includes('node_modules'));
389
- const result = await getChangedActors({
399
+ const result = await changedActorsFor({
390
400
  filepathsChanged: ['actors/foo_bar/src/main.ts'],
391
401
  actorConfigs: [miniActor],
392
- commits,
402
+ range,
393
403
  });
394
404
  expect(result).toEqual([miniActor]);
395
405
  });
396
406
 
397
407
  it('test file listed in .dockerignore triggers a build when it matches the test files glob', async () => {
398
408
  vi.spyOn(Dockerignore, 'loadDockerIgnore').mockReturnValue((filePath) => filePath.startsWith('test/'));
399
- const result = await getChangedActors({
409
+ const result = await changedActorsFor({
400
410
  filepathsChanged: ['test/platform/daily/foo.test.ts'],
401
411
  actorConfigs: [miniActor],
402
- commits,
412
+ range,
403
413
  testFilesGlob: 'test/platform/**',
404
414
  });
405
415
  expect(result).toEqual([miniActor]);
@@ -407,28 +417,28 @@ describe('getChangedActors', () => {
407
417
 
408
418
  it('test file listed in .dockerignore stays ignored without a test files glob', async () => {
409
419
  vi.spyOn(Dockerignore, 'loadDockerIgnore').mockReturnValue((filePath) => filePath.startsWith('test/'));
410
- const result = await getChangedActors({
420
+ const result = await changedActorsFor({
411
421
  filepathsChanged: ['test/platform/daily/foo.test.ts'],
412
422
  actorConfigs: [miniActor],
413
- commits,
423
+ range,
414
424
  });
415
425
  expect(result).toEqual([]);
416
426
  });
417
427
 
418
428
  it('test files glob matches case-insensitively and does not bypass .dockerignore for other files', async () => {
419
429
  vi.spyOn(Dockerignore, 'loadDockerIgnore').mockReturnValue(() => true);
420
- const result = await getChangedActors({
430
+ const result = await changedActorsFor({
421
431
  filepathsChanged: ['Test/Platform/Foo.test.ts'],
422
432
  actorConfigs: [miniActor],
423
- commits,
433
+ range,
424
434
  testFilesGlob: 'test/platform/**',
425
435
  });
426
436
  expect(result).toEqual([miniActor]);
427
437
  expect(
428
- await getChangedActors({
438
+ await changedActorsFor({
429
439
  filepathsChanged: ['actors/foo_bar/node_modules/foo.js'],
430
440
  actorConfigs: [miniActor],
431
- commits,
441
+ range,
432
442
  testFilesGlob: 'test/platform/**',
433
443
  }),
434
444
  ).toEqual([]);
@@ -436,10 +446,10 @@ describe('getChangedActors', () => {
436
446
 
437
447
  it('test file outside an actor context still does not trigger it', async () => {
438
448
  vi.spyOn(Dockerignore, 'loadDockerIgnore').mockReturnValue(() => true);
439
- const result = await getChangedActors({
449
+ const result = await changedActorsFor({
440
450
  filepathsChanged: ['test/platform/daily/foo.test.ts'],
441
451
  actorConfigs: [standaloneActor],
442
- commits,
452
+ range,
443
453
  testFilesGlob: 'test/platform/**',
444
454
  });
445
455
  expect(result).toEqual([]);
@@ -451,85 +461,85 @@ describe('getChangedActors', () => {
451
461
  ...standaloneActor,
452
462
  contextPaths: [...standaloneActor.contextPaths, new RelativeDir('test')],
453
463
  };
454
- const result = await getChangedActors({
464
+ const result = await changedActorsFor({
455
465
  filepathsChanged: ['test/platform/standalone.test.ts'],
456
466
  actorConfigs: [actorWithTests],
457
- commits,
467
+ range,
458
468
  testFilesGlob: 'test/platform/**',
459
469
  });
460
470
  expect(result).toEqual([actorWithTests]);
461
471
  });
462
472
 
463
473
  it('JSON file in context but outside actor folder is functional (not checked for cosmetic)', async () => {
464
- const result = await getChangedActors({
474
+ const result = await changedActorsFor({
465
475
  filepathsChanged: ['lib/config.json'],
466
476
  actorConfigs: [miniActor],
467
- commits,
477
+ range,
468
478
  });
469
479
  expect(result).toEqual([miniActor]);
470
480
  });
471
481
 
472
482
  it('README outside actor folder but inside context is ignored (not cosmetic)', async () => {
473
- const result = await getChangedActors({
483
+ const result = await changedActorsFor({
474
484
  filepathsChanged: ['docs/README.md'],
475
485
  actorConfigs: [miniActor],
476
- commits,
486
+ range,
477
487
  isLatest: true,
478
488
  });
479
489
  expect(result).toEqual([]);
480
490
  });
481
491
 
482
492
  it('README inside actor folder is cosmetic', async () => {
483
- const result = await getChangedActors({
493
+ const result = await changedActorsFor({
484
494
  filepathsChanged: ['actors/foo_bar/README.md'],
485
495
  actorConfigs: [miniActor],
486
- commits,
496
+ range,
487
497
  isLatest: true,
488
498
  });
489
499
  expect(result).toEqual([miniActor]);
490
500
  });
491
501
 
492
502
  it('ignores a standalone actor top-level dev file relative to its context', async () => {
493
- const result = await getChangedActors({
503
+ const result = await changedActorsFor({
494
504
  filepathsChanged: ['standalone-actors/standalone/.eslintrc'],
495
505
  actorConfigs,
496
- commits,
506
+ range,
497
507
  });
498
508
  expect(result).toEqual([]);
499
509
  });
500
510
 
501
511
  it('does not special-case code/ and shared/ prefixes anymore — must be declared via overrideActorContext', async () => {
502
- const result = await getChangedActors({
512
+ const result = await changedActorsFor({
503
513
  filepathsChanged: ['code/some/code.ts'],
504
514
  actorConfigs: [miniActor],
505
- commits,
515
+ range,
506
516
  });
507
517
  expect(result).toEqual([miniActor]);
508
518
  });
509
519
 
510
520
  it('ignores code/.eslintrc when "code" is declared via overrideActorContext', async () => {
511
- const result = await getChangedActors({
521
+ const result = await changedActorsFor({
512
522
  filepathsChanged: ['code/.eslintrc'],
513
523
  actorConfigs: [amazonActor],
514
- commits,
524
+ range,
515
525
  });
516
526
  expect(result).toEqual([]);
517
527
  });
518
528
 
519
529
  it('shared/Dockerfile declared via overrideActorContext is functional', async () => {
520
- const result = await getChangedActors({
530
+ const result = await changedActorsFor({
521
531
  filepathsChanged: ['shared/Dockerfile'],
522
532
  actorConfigs: [amazonActor],
523
- commits,
533
+ range,
524
534
  });
525
535
  expect(result).toEqual([amazonActor]);
526
536
  });
527
537
 
528
538
  it('code/README.md declared via overrideActorContext is ignored (outside actor folder)', async () => {
529
- const result = await getChangedActors({
539
+ const result = await changedActorsFor({
530
540
  filepathsChanged: ['code/README.md'],
531
541
  actorConfigs: [amazonActor],
532
- commits,
542
+ range,
533
543
  isLatest: true,
534
544
  });
535
545
  expect(result).toEqual([]);
@@ -547,13 +557,14 @@ describe('getChangedActors logging', () => {
547
557
  await getChangedActors({
548
558
  filepathsChanged: ['actors/foo_bar/src/main.ts'],
549
559
  actorConfigs: [miniActor],
550
- commits,
560
+ range,
551
561
  });
552
562
 
553
563
  expect(logger.info).toHaveBeenCalledWith(
554
564
  '[DIFF]: Changes specific to actor foo/bar: actors/foo_bar/src/main.ts',
555
565
  );
556
- expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors to be built and tested: foo/bar');
566
+ expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors with functional changes: foo/bar');
567
+ expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors with only cosmetic changes: none');
557
568
  });
558
569
 
559
570
  it('logs a single "shared" group when two actors are triggered by the exact same file', async () => {
@@ -563,14 +574,14 @@ describe('getChangedActors logging', () => {
563
574
  await getChangedActors({
564
575
  filepathsChanged: ['shared/shared.ts'],
565
576
  actorConfigs: [actorA, actorB],
566
- commits,
577
+ range,
567
578
  });
568
579
 
569
580
  expect(logger.info).toHaveBeenCalledWith(
570
581
  '[DIFF]: Shared changes for actors team/actor-a, team/actor-b: shared/shared.ts',
571
582
  );
572
583
  expect(logger.info).not.toHaveBeenCalledWith(expect.stringContaining('Changes specific to actor'));
573
- expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors to be built and tested: team/actor-a, team/actor-b');
584
+ expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors with functional changes: team/actor-a, team/actor-b');
574
585
  });
575
586
 
576
587
  it('logs shared and specific groups in descending-size order for partial overlap across actors', async () => {
@@ -580,7 +591,7 @@ describe('getChangedActors logging', () => {
580
591
  await getChangedActors({
581
592
  filepathsChanged: ['shared.ts', 'actors/a/a-only.ts', 'actors/b/b-only.ts'],
582
593
  actorConfigs: [actorA, actorB],
583
- commits,
594
+ range,
584
595
  });
585
596
 
586
597
  const errorCalls = vi.mocked(logger.info).mock.calls.map((call) => call[0]);
@@ -589,7 +600,8 @@ describe('getChangedActors logging', () => {
589
600
  '[DIFF]: Shared changes for actors team/actor-a, team/actor-b: shared.ts',
590
601
  '[DIFF]: Changes specific to actor team/actor-a: actors/a/a-only.ts',
591
602
  '[DIFF]: Changes specific to actor team/actor-b: actors/b/b-only.ts',
592
- '[DIFF]: Actors to be built and tested: team/actor-a, team/actor-b',
603
+ '[DIFF]: Actors with functional changes: team/actor-a, team/actor-b',
604
+ '[DIFF]: Actors with only cosmetic changes: none',
593
605
  ]);
594
606
  });
595
607
 
@@ -601,7 +613,7 @@ describe('getChangedActors logging', () => {
601
613
  await getChangedActors({
602
614
  filepathsChanged: ['test/platform/standalone.test.ts'],
603
615
  actorConfigs: [standaloneActor],
604
- commits,
616
+ range,
605
617
  testFilesGlob: 'test/platform/**',
606
618
  });
607
619
 
@@ -609,10 +621,10 @@ describe('getChangedActors logging', () => {
609
621
  });
610
622
 
611
623
  it('still warns when the same push also rebuilds an Actor', async () => {
612
- const result = await getChangedActors({
624
+ const result = await changedActorsFor({
613
625
  filepathsChanged: ['test/platform/standalone.test.ts', 'standalone-actors/standalone/src/main.ts'],
614
626
  actorConfigs: [standaloneActor],
615
- commits,
627
+ range,
616
628
  testFilesGlob: 'test/platform/**',
617
629
  });
618
630
 
@@ -624,7 +636,7 @@ describe('getChangedActors logging', () => {
624
636
  await getChangedActors({
625
637
  filepathsChanged: ['test/platform/foo.test.ts', 'test/platform/README.md', 'test/platform/CHANGELOG.md'],
626
638
  actorConfigs: [miniActor],
627
- commits,
639
+ range,
628
640
  testFilesGlob: 'test/platform/**',
629
641
  });
630
642
 
@@ -635,11 +647,37 @@ describe('getChangedActors logging', () => {
635
647
  await getChangedActors({
636
648
  filepathsChanged: ['.gitignore', '.prettierrc'],
637
649
  actorConfigs,
638
- commits,
650
+ range,
639
651
  });
640
652
 
641
653
  expect(logger.info).not.toHaveBeenCalledWith(expect.stringContaining('Shared changes'));
642
654
  expect(logger.info).not.toHaveBeenCalledWith(expect.stringContaining('Changes specific to'));
643
- expect(logger.info).toHaveBeenCalledWith('[DIFF]: No relevant files changed, skipping builds and tests');
655
+ expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors with functional changes: none');
656
+ expect(logger.info).toHaveBeenCalledWith('[DIFF]: Actors with only cosmetic changes: none');
657
+ });
658
+ });
659
+
660
+ describe('getChangedActors split by impact', () => {
661
+ beforeEach(() => {
662
+ vi.spyOn(DiffJsonSchema, 'isCosmeticOnlyJsonSchemaChange').mockResolvedValue(false);
663
+ });
664
+
665
+ it('lists an Actor as functional when it has any functional change, and as cosmetic only otherwise', async () => {
666
+ const result = await getChangedActors({
667
+ filepathsChanged: [
668
+ 'actors/foo_bar/README.md',
669
+ 'standalone-actors/standalone/README.md',
670
+ 'actors/foo_bar/src/main.ts',
671
+ ],
672
+ actorConfigs,
673
+ range,
674
+ });
675
+ expect(result).toStrictEqual({ functional: [miniActor], cosmetic: [standaloneActor] });
676
+ });
677
+
678
+ it('returns empty lists when nothing relevant changed', async () => {
679
+ expect(
680
+ await getChangedActors({ filepathsChanged: ['.vscode/settings.json'], actorConfigs, range }),
681
+ ).toStrictEqual({ functional: [], cosmetic: [] });
644
682
  });
645
683
  });