apify-test-tools 0.8.6 → 0.9.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -13
- package/bin/build-from-local.ts +35 -34
- package/bin/build.ts +55 -59
- package/bin/diff-changes.ts +166 -125
- package/bin/dockerignore.ts +50 -0
- package/bin/main.ts +7 -7
- package/bin/path-utils.ts +13 -0
- package/bin/test-report.ts +5 -5
- package/bin/types.ts +17 -5
- package/bin/utils.ts +124 -95
- package/dist/bin/build-from-local.d.ts +2 -2
- package/dist/bin/build-from-local.d.ts.map +1 -1
- package/dist/bin/build-from-local.js +33 -28
- package/dist/bin/build-from-local.js.map +1 -1
- package/dist/bin/build.d.ts +5 -8
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +49 -56
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/diff-changes.d.ts +0 -6
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +129 -89
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/dockerignore.d.ts +17 -0
- package/dist/bin/dockerignore.d.ts.map +1 -0
- package/dist/bin/dockerignore.js +41 -0
- package/dist/bin/dockerignore.js.map +1 -0
- package/dist/bin/main.js +7 -7
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/path-utils.d.ts +4 -0
- package/dist/bin/path-utils.d.ts.map +1 -0
- package/dist/bin/path-utils.js +9 -0
- package/dist/bin/path-utils.js.map +1 -0
- package/dist/bin/test-report.js +3 -3
- package/dist/bin/test-report.js.map +1 -1
- package/dist/bin/types.d.ts +15 -4
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/bin/utils.d.ts +2 -19
- package/dist/bin/utils.d.ts.map +1 -1
- package/dist/bin/utils.js +82 -81
- package/dist/bin/utils.js.map +1 -1
- package/dist/lib/lib.d.ts +10 -4
- package/dist/lib/lib.d.ts.map +1 -1
- package/dist/lib/lib.js +28 -22
- package/dist/lib/lib.js.map +1 -1
- package/dist/lib/types.d.ts +2 -2
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +4 -4
- package/dist/lib/utils.js.map +1 -1
- package/dist/test/unit/bin/build-from-local.test.js +55 -86
- package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
- package/dist/test/unit/bin/diff-changes.test.js +358 -36
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/dockerignore.test.d.ts +2 -0
- package/dist/test/unit/bin/dockerignore.test.d.ts.map +1 -0
- package/dist/test/unit/bin/dockerignore.test.js +102 -0
- package/dist/test/unit/bin/dockerignore.test.js.map +1 -0
- package/dist/test/unit/bin/path-utils.test.d.ts +2 -0
- package/dist/test/unit/bin/path-utils.test.d.ts.map +1 -0
- package/dist/test/unit/bin/path-utils.test.js +14 -0
- package/dist/test/unit/bin/path-utils.test.js.map +1 -0
- package/dist/test/unit/bin/utils.test.d.ts +2 -0
- package/dist/test/unit/bin/utils.test.d.ts.map +1 -0
- package/dist/test/unit/bin/utils.test.js +292 -0
- package/dist/test/unit/bin/utils.test.js.map +1 -0
- package/dist/test/unit/should-built-and-test.test.js +83 -32
- package/dist/test/unit/should-built-and-test.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/lib.ts +28 -22
- package/lib/types.ts +2 -2
- package/lib/utils.ts +4 -4
- package/package.json +62 -61
- package/test/unit/bin/build-from-local.test.ts +62 -121
- package/test/unit/bin/diff-changes.test.ts +397 -41
- package/test/unit/bin/dockerignore.test.ts +123 -0
- package/test/unit/bin/path-utils.test.ts +17 -0
- package/test/unit/bin/utils.test.ts +342 -0
- package/test/unit/should-built-and-test.test.ts +89 -32
package/lib/lib.ts
CHANGED
|
@@ -24,8 +24,8 @@ try {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
const config = actorBuilds.reduce<Map<string, ActorBuild>>((map, cfg) => {
|
|
27
|
-
map.set(cfg.
|
|
28
|
-
map.set(cfg.
|
|
27
|
+
map.set(cfg.actorFullName, cfg);
|
|
28
|
+
map.set(cfg.actorRawId, cfg);
|
|
29
29
|
return map;
|
|
30
30
|
}, new Map<string, ActorBuild>());
|
|
31
31
|
|
|
@@ -51,8 +51,11 @@ const DEFAULT_TEST_ACTOR_OPTIONS: ActorTestOptions = {
|
|
|
51
51
|
timeout: DEFAULT_TEST_RUN_DURATION_MS,
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @param actorId - The actor's raw platform ID or its full name (`owner/name`, e.g. `"apify/web-scraper"`).
|
|
56
|
+
*/
|
|
54
57
|
export const testActor = <T>(
|
|
55
|
-
|
|
58
|
+
actorId: string,
|
|
56
59
|
testName: string,
|
|
57
60
|
fn: TestFunction<{ run: ReturnType<typeof createStartRunFn<T>> }>,
|
|
58
61
|
testOptions?: ActorTestOptions,
|
|
@@ -61,13 +64,13 @@ export const testActor = <T>(
|
|
|
61
64
|
...DEFAULT_TEST_ACTOR_OPTIONS,
|
|
62
65
|
...testOptions,
|
|
63
66
|
};
|
|
64
|
-
const name = `${
|
|
65
|
-
const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(
|
|
67
|
+
const name = `${actorId}: ${testName}`;
|
|
68
|
+
const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorId);
|
|
66
69
|
vitestTest.runIf(shouldRun)(name, options, async <TYPE extends TestContext>(context: TYPE) => {
|
|
67
70
|
const { expect, ...rest } = context;
|
|
68
71
|
await fn({
|
|
69
72
|
expect: extendExpect(expect),
|
|
70
|
-
run: createStartRunFn(
|
|
73
|
+
run: createStartRunFn(actorId, context),
|
|
71
74
|
...rest,
|
|
72
75
|
});
|
|
73
76
|
});
|
|
@@ -79,9 +82,12 @@ export const testActor = <T>(
|
|
|
79
82
|
*
|
|
80
83
|
* Using task is just current shortcoming of standby feature but ideally we would use Actor directly
|
|
81
84
|
*/
|
|
85
|
+
/**
|
|
86
|
+
* @param actorId - The actor's raw platform ID or its full name (`owner/name`, e.g. `"apify/web-scraper"`).
|
|
87
|
+
*/
|
|
82
88
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
89
|
export const testStandbyActor = <I = any, O = any>(
|
|
84
|
-
|
|
90
|
+
actorId: string,
|
|
85
91
|
testName: string,
|
|
86
92
|
fn: TestFunction<{ callStandby: ReturnType<typeof createStartStandbyFn<I, O>> }>,
|
|
87
93
|
testOptions?: ActorTestOptions,
|
|
@@ -90,11 +96,11 @@ export const testStandbyActor = <I = any, O = any>(
|
|
|
90
96
|
...DEFAULT_TEST_ACTOR_OPTIONS,
|
|
91
97
|
...testOptions,
|
|
92
98
|
};
|
|
93
|
-
const name = `${
|
|
94
|
-
const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(
|
|
99
|
+
const name = `${actorId}: ${testName}`;
|
|
100
|
+
const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorId);
|
|
95
101
|
|
|
96
102
|
vitestTest.runIf(shouldRun)(name, options, async <T extends TestContext>(context: T) => {
|
|
97
|
-
const standbyTask = await createStandbyTask(
|
|
103
|
+
const standbyTask = await createStandbyTask(actorId, config.get(actorId)?.buildNumber);
|
|
98
104
|
const { expect, ...rest } = context;
|
|
99
105
|
|
|
100
106
|
// NOTE: we wrap `fn` in try/finally so cleanup (deleting the task) always runs afterwards
|
|
@@ -193,20 +199,20 @@ interface StandbyTask {
|
|
|
193
199
|
*
|
|
194
200
|
* @throws if actor doesn't exist or it doesn't support standby mode.
|
|
195
201
|
*/
|
|
196
|
-
const createStandbyTask = async (
|
|
197
|
-
const actor = apifyClient.actor(
|
|
202
|
+
const createStandbyTask = async (actorId: string, buildNumber?: string): Promise<StandbyTask> => {
|
|
203
|
+
const actor = apifyClient.actor(actorId);
|
|
198
204
|
|
|
199
205
|
const actorInfo = (await actor.get()) as Actor & { standbyUrl?: string };
|
|
200
206
|
if (!actorInfo) {
|
|
201
|
-
throw new Error(`Actor "${
|
|
207
|
+
throw new Error(`Actor "${actorId}" not found`);
|
|
202
208
|
}
|
|
203
209
|
|
|
204
210
|
if (!actorInfo.standbyUrl) {
|
|
205
|
-
throw new Error(`Actor "${
|
|
211
|
+
throw new Error(`Actor "${actorId}" doesn't support standby mode`);
|
|
206
212
|
}
|
|
207
213
|
|
|
208
214
|
if (!actorInfo.actorStandby) {
|
|
209
|
-
throw new Error(`Actor "${
|
|
215
|
+
throw new Error(`Actor "${actorId} doesn't contain actorStandby options`);
|
|
210
216
|
}
|
|
211
217
|
const { isEnabled, ...defaultActorStandby } = actorInfo.actorStandby;
|
|
212
218
|
delete defaultActorStandby.disableStandbyFieldsOverride;
|
|
@@ -225,9 +231,9 @@ const createStandbyTask = async (actorNameOrId: string, buildNumber?: string): P
|
|
|
225
231
|
const randomValue = Math.random().toString(10).slice(2).padEnd(randomValueLength, '0');
|
|
226
232
|
const name = `test-${randomValue.slice(0, randomValueLength)}`;
|
|
227
233
|
const newTask = (await apifyClient.tasks().create({
|
|
228
|
-
actId:
|
|
234
|
+
actId: actorId,
|
|
229
235
|
actorStandby: actorStandbyOptions,
|
|
230
|
-
description: `Task for testing standby version ${build} of actor "${
|
|
236
|
+
description: `Task for testing standby version ${build} of actor "${actorId}"`,
|
|
231
237
|
title,
|
|
232
238
|
name,
|
|
233
239
|
})) as Task & { standbyUrl?: string };
|
|
@@ -247,9 +253,9 @@ const createStandbyTask = async (actorNameOrId: string, buildNumber?: string): P
|
|
|
247
253
|
}
|
|
248
254
|
};
|
|
249
255
|
|
|
250
|
-
const createStartRunFn = <T>(
|
|
256
|
+
const createStartRunFn = <T>(actorId: string, testContext: TestContext) => {
|
|
251
257
|
const { annotate, task } = testContext;
|
|
252
|
-
const actorConfig = config.get(
|
|
258
|
+
const actorConfig = config.get(actorId);
|
|
253
259
|
const build = actorConfig?.buildNumber;
|
|
254
260
|
const buildId = actorConfig?.buildId;
|
|
255
261
|
return async (runOptions: RunOptions<T>) => {
|
|
@@ -263,10 +269,10 @@ const createStartRunFn = <T>(actorNameOrId: string, testContext: TestContext) =>
|
|
|
263
269
|
return new RunTestResult(apifyClient, run);
|
|
264
270
|
}
|
|
265
271
|
|
|
266
|
-
const actor = apifyClient.actor(
|
|
272
|
+
const actor = apifyClient.actor(actorId);
|
|
267
273
|
|
|
268
274
|
const actorInput = {
|
|
269
|
-
...(prefilledInput && (await getActorPrefilledInput(apifyClient,
|
|
275
|
+
...(prefilledInput && (await getActorPrefilledInput(apifyClient, actorId, buildId))),
|
|
270
276
|
...input,
|
|
271
277
|
};
|
|
272
278
|
const run = await actor.call(actorInput, { build, log: null, ...options });
|
|
@@ -277,7 +283,7 @@ const createStartRunFn = <T>(actorNameOrId: string, testContext: TestContext) =>
|
|
|
277
283
|
task.meta = {
|
|
278
284
|
runId: run.id,
|
|
279
285
|
runLink,
|
|
280
|
-
|
|
286
|
+
actorId: actorConfig?.actorFullName ?? actorId,
|
|
281
287
|
};
|
|
282
288
|
|
|
283
289
|
// waiting for datasetItemCount and chargedEventCounts to sync
|
package/lib/types.ts
CHANGED
package/lib/utils.ts
CHANGED
|
@@ -6,11 +6,11 @@ import type { ApifyClient } from 'apify-client';
|
|
|
6
6
|
*/
|
|
7
7
|
export const getActorPrefilledInput = async (
|
|
8
8
|
apifyClient: ApifyClient,
|
|
9
|
-
|
|
9
|
+
actorId: string,
|
|
10
10
|
buildId: string | undefined,
|
|
11
11
|
) => {
|
|
12
12
|
if (!buildId) {
|
|
13
|
-
const actorInfo = await apifyClient.actor(
|
|
13
|
+
const actorInfo = await apifyClient.actor(actorId).get();
|
|
14
14
|
|
|
15
15
|
const defaultBuildTag = actorInfo?.defaultRunOptions.build;
|
|
16
16
|
|
|
@@ -19,7 +19,7 @@ export const getActorPrefilledInput = async (
|
|
|
19
19
|
buildId = taggedBuild?.buildId;
|
|
20
20
|
|
|
21
21
|
if (!buildId) {
|
|
22
|
-
console.error(`Coudn't find default build for actor ${
|
|
22
|
+
console.error(`Coudn't find default build for actor ${actorId}. Prefilled values will not be used.`);
|
|
23
23
|
return {};
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -34,7 +34,7 @@ export const getActorPrefilledInput = async (
|
|
|
34
34
|
|
|
35
35
|
if (!inputSchema) {
|
|
36
36
|
console.error(
|
|
37
|
-
`Coudn't find input schema definition for actor ${
|
|
37
|
+
`Coudn't find input schema definition for actor ${actorId}, build ${buildId}.`,
|
|
38
38
|
'Prefilled values will not be used',
|
|
39
39
|
);
|
|
40
40
|
return {};
|
package/package.json
CHANGED
|
@@ -1,63 +1,64 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
2
|
+
"name": "apify-test-tools",
|
|
3
|
+
"version": "0.9.0-beta.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "TBD",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/apify/apify-test-tools.git"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18.0.0"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"apify-test-tools": "dist/bin/main.js"
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"main": "./dist/lib/lib.js",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@apify/consts": "^2.43.0",
|
|
20
|
+
"@slack/web-api": "^7.9.2",
|
|
21
|
+
"apify-client": "^2.22.2",
|
|
22
|
+
"ignore": "^7.0.5",
|
|
23
|
+
"yargs": "^18.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@apify/eslint-config": "^1.0.0",
|
|
27
|
+
"@apify/tsconfig": "^0.1.1",
|
|
28
|
+
"@types/node": "^24.0.0",
|
|
29
|
+
"@types/yargs": "^17.0.33",
|
|
30
|
+
"eslint": "^9.29.0",
|
|
31
|
+
"eslint-config-prettier": "^10.1.5",
|
|
32
|
+
"globals": "^17.0.0",
|
|
33
|
+
"husky": "^9.0.11",
|
|
34
|
+
"knip": "^5.65.0",
|
|
35
|
+
"lint-staged": "^15.2.2",
|
|
36
|
+
"prettier": "^3.5.3",
|
|
37
|
+
"tsx": "^4.20.3",
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"typescript-eslint": "^8.34.1",
|
|
40
|
+
"vitest": "^3.2.4"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"vitest": ">=3.2.4"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"start": "npm run dist/bin/main.js",
|
|
47
|
+
"start:dev": "GITHUB_WORKSPACE=local-clone tsx bin/main.ts",
|
|
48
|
+
"build": "tsc && chmod +x ./dist/bin/main.js",
|
|
49
|
+
"lint": "eslint",
|
|
50
|
+
"lint:fix": "eslint --fix",
|
|
51
|
+
"format": "prettier --write .",
|
|
52
|
+
"format:check": "prettier --check .",
|
|
53
|
+
"type-check": "tsc --noEmit",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"check-unused": "npx knip",
|
|
56
|
+
"prepare": "husky .husky || true"
|
|
57
|
+
},
|
|
58
|
+
"lint-staged": {
|
|
59
|
+
"*.js": "eslint",
|
|
60
|
+
"*.ts": "eslint"
|
|
61
|
+
},
|
|
62
|
+
"author": "oklinov",
|
|
63
|
+
"license": "ISC"
|
|
63
64
|
}
|
|
@@ -8,6 +8,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
collectNonIgnoredFiles,
|
|
11
|
+
collectSourceFiles,
|
|
11
12
|
flattenMonorepoContext,
|
|
12
13
|
rewriteActorJsonPaths,
|
|
13
14
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -62,86 +63,76 @@ describe('build-from-local helpers', () => {
|
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
describe('flattenMonorepoContext', () => {
|
|
65
|
-
it(
|
|
66
|
+
it("overlays the actor's own .actor/ files at the flattened root", async () => {
|
|
67
|
+
// Simulates flattening from a monorepo with an actors/<name>/.actor structure.
|
|
66
68
|
const repoRoot = await mkTempDir('apify-test-tools-repo-');
|
|
67
69
|
tempDirs.push(repoRoot);
|
|
68
|
-
initGitRepo(repoRoot);
|
|
69
70
|
|
|
70
71
|
const absActorDir = path.join(repoRoot, 'actors', 'owner_actor');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
);
|
|
76
|
-
// A stray secret file inside .actor/ must never survive into the build.
|
|
77
|
-
await fs.writeFile(path.join(absActorDir, '.actor', '.env'), 'SECRET=leaked');
|
|
78
|
-
await fs.writeFile(path.join(absActorDir, '.actor', 'INPUT_SCHEMA.json'), '{}');
|
|
72
|
+
const actorJsonPath = path.join(absActorDir, '.actor', 'actor.json');
|
|
73
|
+
const inputSchemaPath = path.join(absActorDir, '.actor', 'INPUT_SCHEMA.json');
|
|
74
|
+
const mainSrcPath = path.join(absActorDir, 'src', 'main.ts');
|
|
75
|
+
const packageJsonPath = path.join(repoRoot, 'package.json');
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
await fs.
|
|
77
|
+
await fs.mkdir(path.join(absActorDir, '.actor'), { recursive: true });
|
|
78
|
+
await fs.mkdir(path.join(absActorDir, 'src'), { recursive: true });
|
|
79
|
+
await fs.writeFile(actorJsonPath, JSON.stringify({ actorSpecification: 1, name: 'actor' }));
|
|
80
|
+
await fs.writeFile(inputSchemaPath, '{}');
|
|
81
|
+
await fs.writeFile(mainSrcPath, 'console.log(1)');
|
|
82
|
+
await fs.writeFile(packageJsonPath, '{}');
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
vi.spyOn(Utils, 'getGitignoredPaths').mockReturnValue(new Set());
|
|
84
|
+
const keptContextFiles = [packageJsonPath, mainSrcPath, actorJsonPath, inputSchemaPath];
|
|
85
85
|
|
|
86
|
-
const actorJson = JSON.parse(
|
|
87
|
-
await fs.readFile(path.join(absActorDir, '.actor', 'actor.json'), 'utf8'),
|
|
88
|
-
) as Record<string, unknown>;
|
|
86
|
+
const actorJson = JSON.parse(await fs.readFile(actorJsonPath, 'utf8')) as Record<string, unknown>;
|
|
89
87
|
|
|
90
88
|
const { tempDir: flattenedDir, filePaths } = await flattenMonorepoContext(
|
|
91
89
|
'test/actor',
|
|
92
90
|
absActorDir,
|
|
93
91
|
repoRoot,
|
|
94
92
|
actorJson,
|
|
95
|
-
|
|
96
|
-
repoRoot,
|
|
93
|
+
keptContextFiles,
|
|
97
94
|
);
|
|
98
95
|
tempDirs.push(flattenedDir);
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
await expect(fs.access(path.join(flattenedDir, '.actor', 'INPUT_SCHEMA.json'))).resolves.toBeUndefined();
|
|
97
|
+
const flattenedMainSrcPath = path.join(flattenedDir, 'actors', 'owner_actor', 'src', 'main.ts');
|
|
102
98
|
await expect(fs.access(path.join(flattenedDir, '.actor', 'actor.json'))).resolves.toBeUndefined();
|
|
99
|
+
await expect(fs.access(path.join(flattenedDir, '.actor', 'INPUT_SCHEMA.json'))).resolves.toBeUndefined();
|
|
103
100
|
await expect(fs.access(path.join(flattenedDir, 'package.json'))).resolves.toBeUndefined();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
expect(
|
|
107
|
-
|
|
108
|
-
path.join(flattenedDir, 'package.json'),
|
|
109
|
-
path.join(flattenedDir, '.actor', 'INPUT_SCHEMA.json'),
|
|
110
|
-
path.join(flattenedDir, '.actor', 'actor.json'),
|
|
111
|
-
]),
|
|
112
|
-
);
|
|
101
|
+
await expect(fs.access(flattenedMainSrcPath)).resolves.toBeUndefined();
|
|
102
|
+
expect(filePaths).toContain(path.join(flattenedDir, '.actor', 'actor.json'));
|
|
103
|
+
expect(filePaths).toContain(path.join(flattenedDir, '.actor', 'INPUT_SCHEMA.json'));
|
|
104
|
+
expect(filePaths).toContain(flattenedMainSrcPath);
|
|
113
105
|
});
|
|
114
106
|
|
|
115
107
|
it("keeps another actor's .actor/ directory intact at its original nested path", async () => {
|
|
108
|
+
// Simulates flattening from a monorepo with an actors/<name>/.actor structure — two
|
|
109
|
+
// sibling actors share the same context, but only one is being flattened here.
|
|
116
110
|
const repoRoot = await mkTempDir('apify-test-tools-repo-');
|
|
117
111
|
tempDirs.push(repoRoot);
|
|
118
|
-
initGitRepo(repoRoot);
|
|
119
112
|
|
|
120
113
|
const absActorDir = path.join(repoRoot, 'actors', 'owner_actor');
|
|
121
|
-
|
|
122
|
-
await fs.writeFile(
|
|
123
|
-
path.join(absActorDir, '.actor', 'actor.json'),
|
|
124
|
-
JSON.stringify({ actorSpecification: 1, name: 'actor' }),
|
|
125
|
-
);
|
|
126
|
-
|
|
114
|
+
const actorJsonPath = path.join(absActorDir, '.actor', 'actor.json');
|
|
127
115
|
const otherActorDir = path.join(repoRoot, 'actors', 'owner_other-actor');
|
|
128
|
-
await fs.mkdir(path.join(otherActorDir, '.actor'), { recursive: true });
|
|
129
116
|
const otherActorSchemaFile = path.join(otherActorDir, '.actor', 'input_schema.json');
|
|
117
|
+
|
|
118
|
+
await fs.mkdir(path.join(absActorDir, '.actor'), { recursive: true });
|
|
119
|
+
await fs.writeFile(actorJsonPath, JSON.stringify({ actorSpecification: 1, name: 'actor' }));
|
|
120
|
+
await fs.mkdir(path.join(otherActorDir, '.actor'), { recursive: true });
|
|
130
121
|
await fs.writeFile(otherActorSchemaFile, JSON.stringify({ schema: 'other' }));
|
|
131
122
|
|
|
132
|
-
|
|
123
|
+
// Stands in for collectNonIgnoredFiles's already-filtered output: the current actor's own
|
|
124
|
+
// actor.json (always kept by collectNonIgnoredFiles's .actor/ bypass in the real flow —
|
|
125
|
+
// see the collectSourceFiles test below) plus the other actor's file.
|
|
126
|
+
const keptContextFiles = [actorJsonPath, otherActorSchemaFile];
|
|
133
127
|
|
|
134
|
-
const actorJson = JSON.parse(
|
|
135
|
-
await fs.readFile(path.join(absActorDir, '.actor', 'actor.json'), 'utf8'),
|
|
136
|
-
) as Record<string, unknown>;
|
|
128
|
+
const actorJson = JSON.parse(await fs.readFile(actorJsonPath, 'utf8')) as Record<string, unknown>;
|
|
137
129
|
|
|
138
130
|
const { tempDir: flattenedDir, filePaths } = await flattenMonorepoContext(
|
|
139
131
|
'test/actor',
|
|
140
132
|
absActorDir,
|
|
141
133
|
repoRoot,
|
|
142
134
|
actorJson,
|
|
143
|
-
|
|
144
|
-
repoRoot,
|
|
135
|
+
keptContextFiles,
|
|
145
136
|
);
|
|
146
137
|
tempDirs.push(flattenedDir);
|
|
147
138
|
|
|
@@ -151,6 +142,33 @@ describe('build-from-local helpers', () => {
|
|
|
151
142
|
});
|
|
152
143
|
});
|
|
153
144
|
|
|
145
|
+
describe('collectSourceFiles', () => {
|
|
146
|
+
it("always collects the actor's own .actor/actor.json for a monorepo actor, since flattenMonorepoContext depends on it", async () => {
|
|
147
|
+
const repoRoot = await mkTempDir('apify-test-tools-collect-source-');
|
|
148
|
+
tempDirs.push(repoRoot);
|
|
149
|
+
initGitRepo(repoRoot);
|
|
150
|
+
|
|
151
|
+
const originalCwd = process.cwd();
|
|
152
|
+
// We simulate the working directory being the repo root, since collectSourceFiles uses relative paths to the repo root.
|
|
153
|
+
Utils.setCwd({ workspace: repoRoot });
|
|
154
|
+
try {
|
|
155
|
+
const cwd = process.cwd();
|
|
156
|
+
const actorDir = path.join(cwd, 'actors', 'owner_actor');
|
|
157
|
+
await fs.mkdir(path.join(actorDir, '.actor'), { recursive: true });
|
|
158
|
+
await fs.writeFile(
|
|
159
|
+
path.join(actorDir, '.actor', 'actor.json'),
|
|
160
|
+
JSON.stringify({ actorSpecification: 1, name: 'actor', dockerContextDir: '../../..' }),
|
|
161
|
+
);
|
|
162
|
+
await fs.writeFile(path.join(cwd, 'package.json'), '{}');
|
|
163
|
+
|
|
164
|
+
const sourceFiles = await collectSourceFiles('owner/actor', actorDir);
|
|
165
|
+
expect(sourceFiles.map((file) => file.name)).toContain('.actor/actor.json');
|
|
166
|
+
} finally {
|
|
167
|
+
process.chdir(originalCwd);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
154
172
|
describe('rewriteActorJsonPaths', () => {
|
|
155
173
|
it('rewrites path fields that escape .actor/ relative to the new flattened location', async () => {
|
|
156
174
|
const repoRoot = await mkTempDir('apify-test-tools-rewrite-');
|
|
@@ -182,83 +200,6 @@ describe('build-from-local helpers', () => {
|
|
|
182
200
|
});
|
|
183
201
|
});
|
|
184
202
|
|
|
185
|
-
describe('getDockerignoredPaths', () => {
|
|
186
|
-
const tempDirs: string[] = [];
|
|
187
|
-
|
|
188
|
-
afterEach(async () => {
|
|
189
|
-
vi.mocked(spawnSync).mockClear();
|
|
190
|
-
await Promise.all(tempDirs.splice(0).map(async (dir) => fs.rm(dir, { recursive: true, force: true })));
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('returns an empty set without calling git when given no paths', () => {
|
|
194
|
-
const result = Utils.getDockerignoredPaths('/does/not/matter', []);
|
|
195
|
-
|
|
196
|
-
expect(result).toStrictEqual(new Set());
|
|
197
|
-
expect(spawnSync).not.toHaveBeenCalled();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('returns an empty set when rootDir has no .dockerignore', async () => {
|
|
201
|
-
const rootDir = await mkTempDir('apify-test-tools-dockerignore-');
|
|
202
|
-
tempDirs.push(rootDir);
|
|
203
|
-
initGitRepo(rootDir);
|
|
204
|
-
|
|
205
|
-
expect(Utils.getDockerignoredPaths(rootDir, ['main.js'])).toStrictEqual(new Set());
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('returns the paths that match .dockerignore patterns rooted at rootDir', async () => {
|
|
209
|
-
const rootDir = await mkTempDir('apify-test-tools-dockerignore-');
|
|
210
|
-
tempDirs.push(rootDir);
|
|
211
|
-
initGitRepo(rootDir);
|
|
212
|
-
|
|
213
|
-
await fs.writeFile(path.join(rootDir, '.dockerignore'), 'test/\n*.log\n');
|
|
214
|
-
|
|
215
|
-
const result = Utils.getDockerignoredPaths(rootDir, ['main.js', 'test/fixture.json', 'debug.log']);
|
|
216
|
-
|
|
217
|
-
expect(result).toStrictEqual(new Set(['test/fixture.json', 'debug.log']));
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('matches "./"-prefixed patterns, which Docker treats as a no-op but git treats as literal text', async () => {
|
|
221
|
-
const rootDir = await mkTempDir('apify-test-tools-dockerignore-');
|
|
222
|
-
tempDirs.push(rootDir);
|
|
223
|
-
initGitRepo(rootDir);
|
|
224
|
-
|
|
225
|
-
// The common real-world .dockerignore style: every pattern prefixed with "./",
|
|
226
|
-
// including a negation re-including one of the otherwise-matched files.
|
|
227
|
-
await fs.writeFile(path.join(rootDir, '.dockerignore'), './node_modules\n./*.log\n!./keep.log\n');
|
|
228
|
-
|
|
229
|
-
const result = Utils.getDockerignoredPaths(rootDir, [
|
|
230
|
-
'node_modules/foo.js',
|
|
231
|
-
'debug.log',
|
|
232
|
-
'keep.log',
|
|
233
|
-
'main.js',
|
|
234
|
-
]);
|
|
235
|
-
|
|
236
|
-
expect(result).toStrictEqual(new Set(['node_modules/foo.js', 'debug.log']));
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('matches already-tracked files too, since Docker excludes them regardless of git tracking', async () => {
|
|
240
|
-
const rootDir = await mkTempDir('apify-test-tools-dockerignore-');
|
|
241
|
-
tempDirs.push(rootDir);
|
|
242
|
-
initGitRepo(rootDir);
|
|
243
|
-
spawnSync('git', ['config', 'user.email', 'test@example.com'], { cwd: rootDir });
|
|
244
|
-
spawnSync('git', ['config', 'user.name', 'Test'], { cwd: rootDir });
|
|
245
|
-
|
|
246
|
-
// Husky hooks are committed to the repo, so this path is tracked by git — by default
|
|
247
|
-
// `git check-ignore` never reports a tracked file as ignored, which would otherwise mask
|
|
248
|
-
// this exact pattern from matching, unlike a real Docker build which excludes it anyway.
|
|
249
|
-
await fs.mkdir(path.join(rootDir, '.husky'));
|
|
250
|
-
await fs.writeFile(path.join(rootDir, '.husky', 'pre-commit'), '#!/bin/sh\n');
|
|
251
|
-
spawnSync('git', ['add', '.husky/pre-commit'], { cwd: rootDir });
|
|
252
|
-
spawnSync('git', ['commit', '-q', '-m', 'add husky hook'], { cwd: rootDir });
|
|
253
|
-
|
|
254
|
-
await fs.writeFile(path.join(rootDir, '.dockerignore'), './.husky\n');
|
|
255
|
-
|
|
256
|
-
const result = Utils.getDockerignoredPaths(rootDir, ['.husky/pre-commit', 'main.js']);
|
|
257
|
-
|
|
258
|
-
expect(result).toStrictEqual(new Set(['.husky/pre-commit']));
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
203
|
describe('getGitignoredPaths', () => {
|
|
263
204
|
beforeEach(() => {
|
|
264
205
|
vi.mocked(spawnSync).mockReset();
|