apify-test-tools 0.10.2-beta.0 → 0.10.2-beta.10

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