apify-test-tools 0.9.1-beta.2 → 0.9.1-beta.4
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/CHANGELOG.md +4 -0
- package/README.md +1 -3
- package/dist/lib/consts.d.ts +1 -0
- package/dist/lib/consts.d.ts.map +1 -1
- package/dist/lib/consts.js +3 -0
- package/dist/lib/consts.js.map +1 -1
- package/dist/lib/lib.d.ts +10 -0
- package/dist/lib/lib.d.ts.map +1 -1
- package/dist/lib/lib.js +24 -18
- package/dist/lib/lib.js.map +1 -1
- package/dist/lib/types.d.ts +11 -3
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/test/unit/lib.test.d.ts +2 -0
- package/dist/test/unit/lib.test.d.ts.map +1 -0
- package/dist/test/unit/lib.test.js +37 -0
- package/dist/test/unit/lib.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/consts.ts +4 -0
- package/lib/lib.ts +32 -20
- package/lib/types.ts +11 -3
- package/package.json +1 -1
- package/test/unit/lib.test.ts +53 -0
package/lib/consts.ts
CHANGED
|
@@ -17,6 +17,10 @@ export const TO_FINISH_WITH_OPTIONS: ToFinishWithOptionsWithDefaults = {
|
|
|
17
17
|
*/
|
|
18
18
|
export const DEFAULT_TEST_RUN_DURATION_MS = 60 * 60 * 1000; // 1 hour
|
|
19
19
|
|
|
20
|
+
// Prevent orphaned runs - timeout the Actor 1 minute before the test does so everything is logged correctly.
|
|
21
|
+
// - Otherwise the run link URL can get lost.
|
|
22
|
+
export const DEFAULT_TEST_ACTOR_TIMEOUT_SECS = DEFAULT_TEST_RUN_DURATION_MS / 1000 - 60;
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Delay before checking the dataset and statistics after a run finishes to resolve eventual consistency.
|
|
22
26
|
* - This value should ensure that the dataset and statistics are fully updated before any assertions are made.
|
package/lib/lib.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { ApifyClient } from 'apify-client';
|
|
|
3
3
|
import type { SuiteFactory, TestContext, TestFunction } from 'vitest';
|
|
4
4
|
import { describe as vitestDescribe, ExpectStatic, test as vitestTest } from 'vitest';
|
|
5
5
|
|
|
6
|
-
import { DATASET_SYNC_DELAY_MS, DEFAULT_TEST_RUN_DURATION_MS } from './consts.js';
|
|
6
|
+
import { DATASET_SYNC_DELAY_MS, DEFAULT_TEST_ACTOR_TIMEOUT_SECS, DEFAULT_TEST_RUN_DURATION_MS } from './consts.js';
|
|
7
7
|
import { extendExpect } from './extend-expect.js';
|
|
8
8
|
import { RunTestResult } from './run-test-result.js';
|
|
9
9
|
import type { ActorBuild, ActorTestOptions, RunOptions } from './types.js';
|
|
@@ -31,7 +31,7 @@ const config = actorBuilds.reduce<Map<string, ActorBuild>>((map, cfg) => {
|
|
|
31
31
|
|
|
32
32
|
export { ExpectStatic };
|
|
33
33
|
|
|
34
|
-
const { TESTER_APIFY_TOKEN,
|
|
34
|
+
const { TESTER_APIFY_TOKEN, RUN_ALL_PLATFORM_TESTS } = process.env;
|
|
35
35
|
const apifyClient = new ApifyClient({ token: TESTER_APIFY_TOKEN });
|
|
36
36
|
|
|
37
37
|
const DEFAULT_TEST_OPTIONS: ActorTestOptions = {
|
|
@@ -39,16 +39,17 @@ const DEFAULT_TEST_OPTIONS: ActorTestOptions = {
|
|
|
39
39
|
concurrent: true,
|
|
40
40
|
// test should finish within 1 hour
|
|
41
41
|
timeout: DEFAULT_TEST_RUN_DURATION_MS,
|
|
42
|
+
retry: 1,
|
|
42
43
|
};
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Platform tests need `TESTER_APIFY_TOKEN` to talk to the platform, so without it we skip them altogether.
|
|
47
|
+
*
|
|
48
|
+
* `RUN_ALL_PLATFORM_TESTS` enables them too because locally we can test against a hardcoded `runId`,
|
|
49
|
+
* which doesn't need the tester token.
|
|
50
|
+
*/
|
|
44
51
|
export const describe = (name: string, fn?: SuiteFactory<object>, options: ActorTestOptions = DEFAULT_TEST_OPTIONS) => {
|
|
45
|
-
vitestDescribe.runIf(!!
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const DEFAULT_TEST_ACTOR_OPTIONS: ActorTestOptions = {
|
|
49
|
-
retry: 1,
|
|
50
|
-
// prevent orphaned runs
|
|
51
|
-
timeout: DEFAULT_TEST_RUN_DURATION_MS,
|
|
52
|
+
vitestDescribe.runIf(!!TESTER_APIFY_TOKEN || !!RUN_ALL_PLATFORM_TESTS)(name, options, fn);
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
/**
|
|
@@ -60,11 +61,11 @@ export const testActor = <T>(
|
|
|
60
61
|
fn: TestFunction<{ run: ReturnType<typeof createStartRunFn<T>> }>,
|
|
61
62
|
testOptions?: ActorTestOptions,
|
|
62
63
|
) => {
|
|
63
|
-
const options = {
|
|
64
|
-
|
|
65
|
-
...testOptions,
|
|
66
|
-
};
|
|
64
|
+
const options = { ...DEFAULT_TEST_OPTIONS, ...testOptions };
|
|
65
|
+
|
|
67
66
|
const name = `${actorId}: ${testName}`;
|
|
67
|
+
// `RUN_ALL_PLATFORM_TESTS` is needed for the scheduled tests, which have no `ACTOR_BUILDS` to match the
|
|
68
|
+
// tests against - without it, every test would be filtered out as an actor we didn't build.
|
|
68
69
|
const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorId);
|
|
69
70
|
vitestTest.runIf(shouldRun)(name, options, async <TYPE extends TestContext>(context: TYPE) => {
|
|
70
71
|
const { expect, ...rest } = context;
|
|
@@ -92,11 +93,11 @@ export const testStandbyActor = <I = any, O = any>(
|
|
|
92
93
|
fn: TestFunction<{ callStandby: ReturnType<typeof createStartStandbyFn<I, O>> }>,
|
|
93
94
|
testOptions?: ActorTestOptions,
|
|
94
95
|
) => {
|
|
95
|
-
const options = {
|
|
96
|
-
|
|
97
|
-
...testOptions,
|
|
98
|
-
};
|
|
96
|
+
const options = { ...DEFAULT_TEST_OPTIONS, ...testOptions };
|
|
97
|
+
|
|
99
98
|
const name = `${actorId}: ${testName}`;
|
|
99
|
+
// `RUN_ALL_PLATFORM_TESTS` is needed for the scheduled tests, which have no `ACTOR_BUILDS` to match the
|
|
100
|
+
// tests against - without it, every test would be filtered out as an actor we didn't build.
|
|
100
101
|
const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorId);
|
|
101
102
|
|
|
102
103
|
vitestTest.runIf(shouldRun)(name, options, async <T extends TestContext>(context: T) => {
|
|
@@ -269,13 +270,20 @@ const createStartRunFn = <T>(actorId: string, testContext: TestContext) => {
|
|
|
269
270
|
return new RunTestResult(apifyClient, run);
|
|
270
271
|
}
|
|
271
272
|
|
|
272
|
-
const actor = apifyClient.actor(actorId);
|
|
273
|
-
|
|
274
273
|
const actorInput = {
|
|
275
274
|
...(prefilledInput && (await getActorPrefilledInput(apifyClient, actorId, buildId))),
|
|
276
275
|
...input,
|
|
277
276
|
};
|
|
278
|
-
|
|
277
|
+
|
|
278
|
+
const actor = apifyClient.actor(actorId);
|
|
279
|
+
const actorInfo = await actor.get();
|
|
280
|
+
const timeout = Math.min(
|
|
281
|
+
actorInfo?.defaultRunOptions?.timeoutSecs ?? DEFAULT_TEST_ACTOR_TIMEOUT_SECS,
|
|
282
|
+
DEFAULT_TEST_ACTOR_TIMEOUT_SECS,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const actorOptions = { timeout, build, log: null, ...options };
|
|
286
|
+
const run = await actor.call(actorInput, actorOptions);
|
|
279
287
|
|
|
280
288
|
const runLink = generateRunLink(run);
|
|
281
289
|
await annotate(`${task.name} - ${runLink}`, 'run_link');
|
|
@@ -296,3 +304,7 @@ const createStartRunFn = <T>(actorId: string, testContext: TestContext) => {
|
|
|
296
304
|
const generateRunLink = (run: ActorRun | ActorRunListItem): string => {
|
|
297
305
|
return `https://console.apify.com/view/runs/${run.id}`;
|
|
298
306
|
};
|
|
307
|
+
|
|
308
|
+
/** Used for unit testing */
|
|
309
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
310
|
+
export const _private = { createStartRunFn } as const;
|
package/lib/types.ts
CHANGED
|
@@ -10,7 +10,15 @@ export type ActorBuild = {
|
|
|
10
10
|
|
|
11
11
|
export type RunOptions<T> = {
|
|
12
12
|
input: Omit<T, 'actorName'>;
|
|
13
|
-
options?: ActorCallOptions
|
|
13
|
+
options?: Omit<ActorCallOptions, 'timeout'> & {
|
|
14
|
+
/**
|
|
15
|
+
* Timeout for the actor run in seconds. Zero value means there is no timeout.
|
|
16
|
+
* - If `undefined`, the run uses timeout of the default Actor run configuration.
|
|
17
|
+
*
|
|
18
|
+
* @default 3540 // 59 minutes (finish the run before the test)
|
|
19
|
+
*/
|
|
20
|
+
timeout?: number;
|
|
21
|
+
};
|
|
14
22
|
prefilledInput?: boolean;
|
|
15
23
|
/**
|
|
16
24
|
* If you specify `runId`, all the other options will be ignored and this run's data will
|
|
@@ -138,8 +146,8 @@ export type ActorTestOptions = Omit<TestOptions, 'retry' | 'timeout'> & {
|
|
|
138
146
|
// we are just extending the docs here to replace the default value, otherwise it's the exact same
|
|
139
147
|
retry?: TestOptions['retry'];
|
|
140
148
|
/**
|
|
141
|
-
* Timeout for the
|
|
142
|
-
* - If `undefined`, the run uses timeout of the default
|
|
149
|
+
* Timeout for the test in milliseconds. Zero value means there is no timeout.
|
|
150
|
+
* - If `undefined`, the run uses timeout of the default test configuration.
|
|
143
151
|
*
|
|
144
152
|
* @default 60 * 60 * 1000 // 1 hour
|
|
145
153
|
*/
|
package/package.json
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Actor, ActorClient, ActorRun } from 'apify-client';
|
|
2
|
+
import { ApifyClient } from 'apify-client';
|
|
3
|
+
import type { TestContext } from 'vitest';
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { DEFAULT_TEST_ACTOR_TIMEOUT_SECS } from '../../lib/consts.js';
|
|
7
|
+
import { _private } from '../../lib/lib.js';
|
|
8
|
+
import * as UtilsModule from '../../lib/utils.js';
|
|
9
|
+
|
|
10
|
+
const { createStartRunFn } = _private;
|
|
11
|
+
|
|
12
|
+
describe('createStartRunFn()', () => {
|
|
13
|
+
const actorCallMock = vi.fn(async () => Promise.resolve({ id: 'fake-run-id' } as ActorRun));
|
|
14
|
+
const testContext = { task: {}, annotate: vi.fn() as TestContext['annotate'] } as TestContext;
|
|
15
|
+
|
|
16
|
+
vi.spyOn(UtilsModule, 'sleep').mockResolvedValue(undefined);
|
|
17
|
+
|
|
18
|
+
beforeEach(vi.clearAllMocks);
|
|
19
|
+
|
|
20
|
+
it('should apply default timeout to actor runs', async () => {
|
|
21
|
+
// Arrange
|
|
22
|
+
|
|
23
|
+
vi.spyOn(ApifyClient.prototype, 'actor').mockReturnValue({
|
|
24
|
+
call: actorCallMock as ActorClient['call'],
|
|
25
|
+
get: vi.fn(async () => ({ defaultRunOptions: {} }) as Actor) as ActorClient['get'],
|
|
26
|
+
} as ActorClient);
|
|
27
|
+
|
|
28
|
+
// Act
|
|
29
|
+
await createStartRunFn('123', testContext)({ input: {} });
|
|
30
|
+
|
|
31
|
+
// Assert
|
|
32
|
+
expect(actorCallMock).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(actorCallMock).toHaveBeenCalledWith(
|
|
34
|
+
{},
|
|
35
|
+
{ build: undefined, log: null, timeout: DEFAULT_TEST_ACTOR_TIMEOUT_SECS },
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should respect the default actor run timeout if less', async () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
vi.spyOn(ApifyClient.prototype, 'actor').mockReturnValue({
|
|
42
|
+
call: actorCallMock as ActorClient['call'],
|
|
43
|
+
get: vi.fn(async () => ({ defaultRunOptions: { timeoutSecs: 60 } }) as Actor) as ActorClient['get'],
|
|
44
|
+
} as ActorClient);
|
|
45
|
+
|
|
46
|
+
// Act
|
|
47
|
+
await createStartRunFn('123', testContext)({ input: {} });
|
|
48
|
+
|
|
49
|
+
// Assert
|
|
50
|
+
expect(actorCallMock).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(actorCallMock).toHaveBeenCalledWith({}, { build: undefined, log: null, timeout: 60 });
|
|
52
|
+
});
|
|
53
|
+
});
|