apify-test-tools 0.5.0 → 0.5.1

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 (53) hide show
  1. package/.prettierrc +5 -0
  2. package/.vscode/settings.json +3 -0
  3. package/CHANGELOG.md +5 -0
  4. package/bin/git.ts +11 -16
  5. package/bin/github.ts +5 -14
  6. package/bin/main.ts +38 -38
  7. package/bin/slack.ts +13 -16
  8. package/dist/bin/git.d.ts +2 -2
  9. package/dist/bin/git.d.ts.map +1 -1
  10. package/dist/bin/git.js +3 -6
  11. package/dist/bin/git.js.map +1 -1
  12. package/dist/bin/github.d.ts +1 -1
  13. package/dist/bin/github.d.ts.map +1 -1
  14. package/dist/bin/github.js +3 -9
  15. package/dist/bin/github.js.map +1 -1
  16. package/dist/bin/main.js +5 -7
  17. package/dist/bin/main.js.map +1 -1
  18. package/dist/bin/slack.d.ts +2 -2
  19. package/dist/bin/slack.d.ts.map +1 -1
  20. package/dist/bin/slack.js +4 -2
  21. package/dist/bin/slack.js.map +1 -1
  22. package/dist/lib/consts.d.ts +1 -1
  23. package/dist/lib/consts.d.ts.map +1 -1
  24. package/dist/lib/consts.js +1 -4
  25. package/dist/lib/consts.js.map +1 -1
  26. package/dist/lib/extend-expect.d.ts.map +1 -1
  27. package/dist/lib/extend-expect.js +11 -2
  28. package/dist/lib/extend-expect.js.map +1 -1
  29. package/dist/lib/lib.d.ts +1 -1
  30. package/dist/lib/lib.d.ts.map +1 -1
  31. package/dist/lib/lib.js +10 -7
  32. package/dist/lib/lib.js.map +1 -1
  33. package/dist/lib/run-test-result.d.ts +1 -1
  34. package/dist/lib/run-test-result.d.ts.map +1 -1
  35. package/dist/lib/run-test-result.js +1 -0
  36. package/dist/lib/run-test-result.js.map +1 -1
  37. package/dist/lib/types.d.ts +2 -0
  38. package/dist/lib/types.d.ts.map +1 -1
  39. package/dist/test/unit/custom-matchers.test.js +2 -2
  40. package/dist/test/unit/custom-matchers.test.js.map +1 -1
  41. package/dist/test/unit/parse-commit.test.js +5 -5
  42. package/dist/test/unit/parse-commit.test.js.map +1 -1
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/lib/consts.ts +2 -5
  45. package/lib/extend-expect.ts +38 -25
  46. package/lib/lib.ts +29 -40
  47. package/lib/run-test-result.ts +9 -10
  48. package/lib/types.ts +69 -68
  49. package/package.json +4 -1
  50. package/test/unit/custom-matchers.test.ts +6 -9
  51. package/test/unit/parse-commit.test.ts +6 -7
  52. package/tsconfig.json +4 -8
  53. package/test/unit/test-actor.test.ts +0 -17
package/lib/consts.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ToFinishWithOptionsWithDefaults } from './types';
1
+ import type { ToFinishWithOptionsWithDefaults } from './types.js';
2
2
 
3
3
  export const TO_FINISH_WITH_OPTIONS: ToFinishWithOptionsWithDefaults = {
4
4
  status: 'SUCCEEDED',
@@ -8,9 +8,6 @@ export const TO_FINISH_WITH_OPTIONS: ToFinishWithOptionsWithDefaults = {
8
8
  },
9
9
  failedRequests: 0,
10
10
  requestsRetries: { max: 3 },
11
- forbiddenLogs: [
12
- 'ReferenceError',
13
- 'TypeError',
14
- ],
15
11
  maxRetriesPerRequest: null,
12
+ forbiddenLogs: ['ReferenceError', 'TypeError'],
16
13
  };
@@ -1,7 +1,7 @@
1
1
  import type { Assertion, ExpectStatic } from 'vitest';
2
2
 
3
3
  import { TO_FINISH_WITH_OPTIONS } from './consts.js';
4
- import type { Interval, ToFinishWithOptions } from './types';
4
+ import type { Interval, ToFinishWithOptions } from './types.js';
5
5
  import { RunTestResult } from './run-test-result.js';
6
6
 
7
7
  export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
@@ -110,7 +110,7 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
110
110
  },
111
111
  toFinishWith: async <PpeEvent extends string>(
112
112
  received: RunTestResult,
113
- userOptions: ToFinishWithOptions<PpeEvent>,
113
+ userOptions: ToFinishWithOptions<PpeEvent>
114
114
  ) => {
115
115
  const options = {
116
116
  ...TO_FINISH_WITH_OPTIONS,
@@ -145,12 +145,14 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
145
145
 
146
146
  const checkInterval = (
147
147
  value: number | undefined,
148
- key: ['datasetItemCount', 'duration', 'failedRequests', 'requestsRetries', 'maxRetriesPerRequest'][number],
149
- label: string,
148
+ key: ['datasetItemCount', 'duration', 'failedRequests', 'requestsRetries'][number],
149
+ label: string
150
150
  ) => {
151
151
  const result = isWithinInterval(diffs, value, options, key);
152
152
  if (result === false) {
153
- failedAssertions.push(`Failed ${label} check, expected ${JSON.stringify(options[key])}, got ${value}.`);
153
+ failedAssertions.push(
154
+ `Failed ${label} check, expected ${JSON.stringify(options[key])}, got ${value}.`
155
+ );
154
156
  }
155
157
  };
156
158
 
@@ -158,9 +160,20 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
158
160
  checkInterval(durationMillis, 'duration', 'duration');
159
161
  checkInterval(stats?.requestsFailed, 'failedRequests', 'failed requests');
160
162
  checkInterval(stats?.requestsRetries, 'requestsRetries', 'requests retries');
161
- const maxRetriesObserved = (stats?.requestRetryHistogram ?? [0]).length - 1;
162
- checkInterval(maxRetriesObserved, 'maxRetriesPerRequest', 'max retries per request');
163
163
 
164
+ {
165
+ if (options.maxRetriesPerRequest !== null) {
166
+ const maxRetriesPerRequestObserved = (stats?.requestRetryHistogram ?? [0]).length - 1;
167
+ if (maxRetriesPerRequestObserved > options.maxRetriesPerRequest) {
168
+ diffs.pass = false;
169
+ diffs.actual.push(`maxRetriesPerRequest=${maxRetriesPerRequestObserved}`);
170
+ diffs.expected.push(`maxRetriesPerRequest<=${options.maxRetriesPerRequest}`);
171
+ failedAssertions.push(
172
+ `Failed max retries observed check, expected <=${options.maxRetriesPerRequest}, got ${maxRetriesPerRequestObserved}.`
173
+ );
174
+ }
175
+ }
176
+ }
164
177
  const ppeDiffs: Diffs = {
165
178
  pass: true,
166
179
  actual: ['PPE Events:'],
@@ -177,12 +190,7 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
177
190
 
178
191
  for (const ppeEvent of uniquePpeEvents) {
179
192
  if (!(ppeEvent in expected)) {
180
- isWithinInterval(
181
- ppeDiffs,
182
- actual[ppeEvent],
183
- { [ppeEvent]: 0 },
184
- ppeEvent,
185
- );
193
+ isWithinInterval(ppeDiffs, actual[ppeEvent], { [ppeEvent]: 0 }, ppeEvent);
186
194
  continue;
187
195
  }
188
196
  if (!(ppeEvent in actual)) {
@@ -198,7 +206,11 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
198
206
  diffs.pass = ppeDiffs.pass;
199
207
  diffs.actual.push(ppeDiffs.actual.join('\n '));
200
208
  diffs.expected.push(ppeDiffs.expected.join('\n '));
201
- failedAssertions.push(`Failed PPE event counts check, expected ${JSON.stringify(options.chargedEventCounts)}, got ${JSON.stringify(chargedEventCounts)}.`);
209
+ failedAssertions.push(
210
+ `Failed PPE event counts check, expected ${JSON.stringify(
211
+ options.chargedEventCounts
212
+ )}, got ${JSON.stringify(chargedEventCounts)}.`
213
+ );
202
214
  }
203
215
  }
204
216
 
@@ -215,13 +227,15 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
215
227
  diffs.pass = false;
216
228
  diffs.actual.push(` logs=[${occuredLogs.join(', ')}]`);
217
229
  diffs.expected.push(` logs=[]`);
218
- failedAssertions.push(`Failed forbidden logs check, expected [] but got [${occuredLogs.join(', ')}].`);
230
+ failedAssertions.push(
231
+ `Failed forbidden logs check, expected [] but got [${occuredLogs.join(', ')}].`
232
+ );
219
233
  }
220
234
  }
221
235
 
222
236
  return {
223
237
  pass: diffs.pass,
224
- message: () => `Run did not finish as expected. Failed assertions: ${failedAssertions.join(' ',)}`,
238
+ message: () => `Run did not finish as expected. Failed assertions: ${failedAssertions.join(' ')}`,
225
239
  actual: diffs.actual.join('\n '),
226
240
  expected: diffs.expected.join('\n '),
227
241
  };
@@ -257,17 +271,16 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => {
257
271
  };
258
272
 
259
273
  type Diffs = {
260
- pass: boolean
261
- actual: string[]
262
- expected: string[]
263
-
264
- }
274
+ pass: boolean;
275
+ actual: string[];
276
+ expected: string[];
277
+ };
265
278
 
266
279
  const isWithinInterval = <T extends string>(
267
280
  diffs: Diffs,
268
281
  actual: number | undefined,
269
282
  options: Record<T, Interval | null>,
270
- intervalOption: T,
283
+ intervalOption: T
271
284
  ) => {
272
285
  const expected = options[intervalOption];
273
286
  if (expected === null) {
@@ -279,7 +292,7 @@ const isWithinInterval = <T extends string>(
279
292
  diffs.pass = false;
280
293
  diffs.actual.push(`${intervalOption}=${actual}`);
281
294
  diffs.expected.push(`${intervalOption}=${expected}`);
282
- return false
295
+ return false;
283
296
  }
284
297
  } else if (typeof expected === 'object') {
285
298
  const { min, max } = expected;
@@ -287,8 +300,8 @@ const isWithinInterval = <T extends string>(
287
300
  diffs.pass = false;
288
301
  diffs.actual.push(`${intervalOption}=${actual}`);
289
302
  diffs.expected.push(`${intervalOption}=<${min ?? ''},${max ?? ''}>`);
290
- return false
303
+ return false;
291
304
  }
292
305
  }
293
- return
306
+ return;
294
307
  };
package/lib/lib.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  SuiteFactory,
8
8
  TestContext,
9
9
  } from 'vitest';
10
- import type { ActorBuild, ActorTestOptions, RunOptions } from './types';
10
+ import type { ActorBuild, ActorTestOptions, RunOptions } from './types.js';
11
11
  import { RunTestResult } from './run-test-result.js';
12
12
  import { extendExpect } from './extend-expect.js';
13
13
  import { getActorPrefilledInput, sleep } from './utils.js';
@@ -26,7 +26,7 @@ try {
26
26
  throw new Error(`Failed to parse actor builds: ${err}`);
27
27
  }
28
28
 
29
- const config = actorBuilds.reduce((map, cfg) => {
29
+ const config = actorBuilds.reduce<Map<string, ActorBuild>>((map, cfg) => {
30
30
  map.set(cfg.actorName, cfg);
31
31
  map.set(cfg.actorId, cfg);
32
32
  return map;
@@ -44,11 +44,7 @@ const DEFAULT_TEST_OPTIONS: ActorTestOptions = {
44
44
  timeout: 60_000 * 60,
45
45
  };
46
46
 
47
- export const describe = (
48
- name: string,
49
- fn?: SuiteFactory<object>,
50
- options: ActorTestOptions = DEFAULT_TEST_OPTIONS,
51
- ) => {
47
+ export const describe = (name: string, fn?: SuiteFactory<object>, options: ActorTestOptions = DEFAULT_TEST_OPTIONS) => {
52
48
  vitestDescribe.runIf(!!RUN_PLATFORM_TESTS || !!RUN_ALL_PLATFORM_TESTS)(name, options, fn);
53
49
  };
54
50
 
@@ -60,7 +56,7 @@ export const testActor = <T>(
60
56
  actorName: string,
61
57
  testName: string,
62
58
  fn: TestFunction<{ run: ReturnType<typeof createStartRunFn<T>> }>,
63
- testOptions?: ActorTestOptions,
59
+ testOptions?: ActorTestOptions
64
60
  ) => {
65
61
  const options = {
66
62
  ...DEFAULT_TEST_ACTOR_OPTIONS,
@@ -68,8 +64,7 @@ export const testActor = <T>(
68
64
  };
69
65
  const name = `${actorName}: ${testName}`;
70
66
  const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorName);
71
-
72
- vitestTest.runIf(shouldRun)(name, options, async (context) => {
67
+ vitestTest.runIf(shouldRun)(name, options, async <T extends TestContext>(context: T) => {
73
68
  const { expect, ...rest } = context;
74
69
  await fn({
75
70
  expect: extendExpect(expect),
@@ -89,7 +84,7 @@ export const testStandbyActor = <I = any, O = any>(
89
84
  actorName: string,
90
85
  testName: string,
91
86
  fn: TestFunction<{ callStandby: ReturnType<typeof createStartStandbyFn<I, O>> }>,
92
- testOptions?: ActorTestOptions,
87
+ testOptions?: ActorTestOptions
93
88
  ) => {
94
89
  const options = {
95
90
  ...DEFAULT_TEST_ACTOR_OPTIONS,
@@ -98,7 +93,7 @@ export const testStandbyActor = <I = any, O = any>(
98
93
  const name = `${actorName}: ${testName}`;
99
94
  const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorName);
100
95
 
101
- vitestTest.runIf(shouldRun)(name, options, async (context) => {
96
+ vitestTest.runIf(shouldRun)(name, options, async <T extends TestContext>(context: T) => {
102
97
  const standbyTask = await createStandbyTask(actorName, config.get(actorName)?.buildNumber);
103
98
  const { annotate } = context;
104
99
  const { expect, ...rest } = context;
@@ -113,29 +108,28 @@ export const testStandbyActor = <I = any, O = any>(
113
108
  } catch {}
114
109
 
115
110
  const { taskId } = standbyTask;
116
- const runs = (await apifyClient.task(taskId).runs().list()).items
111
+ const runs = (await apifyClient.task(taskId).runs().list()).items;
117
112
  for (const run of runs) {
118
113
  const runLink = generateRunLink(run);
119
114
  await annotate(runLink, 'run_link');
120
115
  }
121
116
 
122
117
  if (taskId) {
123
- await apifyClient.task(taskId).delete()
118
+ await apifyClient.task(taskId).delete();
124
119
  }
125
-
126
120
  });
127
121
  };
128
122
 
129
123
  export const testTestActor = <T>(
130
124
  testName: string,
131
- fn: TestFunction<{ run: ReturnType<typeof createStartRunFn<T>> }>,
125
+ fn: TestFunction<{ run: ReturnType<typeof createStartRunFn<T>> }>
132
126
  ) => {
133
127
  vitestTest(testName, async (context) => {
134
128
  const { expect, ...rest } = context;
135
129
  await fn({
136
130
  expect: extendExpect(expect),
137
131
  // @ts-expect-error: this just to test custom matchers
138
- run: () => { },
132
+ run: () => {},
139
133
  ...rest,
140
134
  });
141
135
  });
@@ -158,14 +152,14 @@ const createStartStandbyFn = <I, O>(standbyTask: StandbyTask) => {
158
152
  body: JSON.stringify(input),
159
153
  });
160
154
 
161
- const data = await response.json() as O;
155
+ const data = (await response.json()) as O;
162
156
  return {
163
157
  data,
164
158
  status: response.status,
165
159
  headers: response.headers,
166
160
  };
167
161
  };
168
- }
162
+ };
169
163
 
170
164
  interface StandbyTask {
171
165
  standbyUrl: string;
@@ -174,7 +168,7 @@ interface StandbyTask {
174
168
 
175
169
  const randomInt = (min: number, max: number) => {
176
170
  return Math.floor(Math.random() * (max - min + 1)) + min;
177
- }
171
+ };
178
172
 
179
173
  /**
180
174
  * Creates a task with specific `build` - either `buildNumber` or default.
@@ -202,19 +196,22 @@ const createStandbyTask = async (actorNameOrId: string, buildNumber?: string): P
202
196
  const actorStandbyOptions: ActorStandby = {
203
197
  ...defaultActorStandby,
204
198
  build,
205
- }
199
+ };
206
200
 
207
201
  try {
208
202
  const title = `Test task - ${build}:${actorNameOrId}`.slice(0, 62);
209
203
  // we try to create unique task name containing only `a-z0-9-` characters and at most 63 characters long
210
- const name = `${randomInt(1, 1_000_000)}${title.toLowerCase().replaceAll(/\s+/g, '').replaceAll(/[^a-z0-9-]+/g, '-')}`.slice(0, 62);
211
- const newTask = await apifyClient.tasks().create({
204
+ const name = `${randomInt(1, 1_000_000)}${title
205
+ .toLowerCase()
206
+ .replaceAll(/\s+/g, '')
207
+ .replaceAll(/[^a-z0-9-]+/g, '-')}`.slice(0, 62);
208
+ const newTask = (await apifyClient.tasks().create({
212
209
  actId: actorNameOrId,
213
210
  actorStandby: actorStandbyOptions,
214
211
  description: `Task for testing standby version ${build}`,
215
212
  title,
216
213
  name,
217
- }) as Task & { standbyUrl?: string };
214
+ })) as Task & { standbyUrl?: string };
218
215
 
219
216
  const { id, standbyUrl } = newTask;
220
217
 
@@ -225,11 +222,11 @@ const createStandbyTask = async (actorNameOrId: string, buildNumber?: string): P
225
222
  return {
226
223
  standbyUrl,
227
224
  taskId: id,
228
- }
225
+ };
229
226
  } catch (error) {
230
227
  throw new Error(`Failed to create task: ${error}`);
231
228
  }
232
- }
229
+ };
233
230
 
234
231
  const createStartRunFn = <T>(actorNameOrId: string, testContext: TestContext) => {
235
232
  const { annotate, task } = testContext;
@@ -237,31 +234,23 @@ const createStartRunFn = <T>(actorNameOrId: string, testContext: TestContext) =>
237
234
  const build = actorConfig?.buildNumber;
238
235
  const buildId = actorConfig?.buildId;
239
236
  return async (runOptions: RunOptions<T>) => {
240
- const {
241
- input,
242
- options,
243
- prefilledInput,
244
- runId,
245
- } = runOptions;
237
+ const { input, options, prefilledInput, runId } = runOptions;
246
238
 
247
239
  if (runId) {
248
- const run = await apifyClient.run(runId).get()
240
+ const run = await apifyClient.run(runId).get();
249
241
  if (!run) {
250
242
  throw new Error(`Run with id "${runId}" doesn't exist`);
251
243
  }
252
- return new RunTestResult(apifyClient, run)
244
+ return new RunTestResult(apifyClient, run);
253
245
  }
254
246
 
255
247
  const actor = apifyClient.actor(actorNameOrId);
256
248
 
257
249
  const actorInput = {
258
- ...(prefilledInput && await getActorPrefilledInput(apifyClient, actorNameOrId, buildId)),
250
+ ...(prefilledInput && (await getActorPrefilledInput(apifyClient, actorNameOrId, buildId))),
259
251
  ...input,
260
252
  };
261
- const run = await actor.call(
262
- actorInput,
263
- { build, ...options, },
264
- );
253
+ const run = await actor.call(actorInput, { build, ...options });
265
254
 
266
255
  const runLink = generateRunLink(run);
267
256
  await annotate(runLink, 'run_link');
@@ -281,4 +270,4 @@ const createStartRunFn = <T>(actorNameOrId: string, testContext: TestContext) =>
281
270
 
282
271
  const generateRunLink = (run: ActorRun | ActorRunListItem): string => {
283
272
  return `https://console.apify.com/view/runs/${run.id}`;
284
- }
273
+ };
@@ -1,5 +1,5 @@
1
1
  import { ActorRun, ApifyClient, KeyValueStoreClient } from 'apify-client';
2
- import type { Dataset, SdkCrawlerStatistics } from './types';
2
+ import type { Dataset, SdkCrawlerStatistics } from './types.js';
3
3
 
4
4
  export class RunTestResult {
5
5
  private log: string | undefined;
@@ -9,10 +9,9 @@ export class RunTestResult {
9
9
  private runInfo: ActorRun | undefined;
10
10
  private input: unknown | undefined;
11
11
 
12
- constructor(
13
- private readonly apifyClient: ApifyClient,
14
- private readonly run: ActorRun,
15
- ) { /**/ }
12
+ constructor(private readonly apifyClient: ApifyClient, private readonly run: ActorRun) {
13
+ /**/
14
+ }
16
15
 
17
16
  getStatistics = async (): Promise<SdkCrawlerStatistics | undefined> => {
18
17
  if (this.statistics) {
@@ -33,7 +32,7 @@ export class RunTestResult {
33
32
  return runLog as string;
34
33
  };
35
34
 
36
- getDataset = async<T>(): Promise<Dataset<T>> => {
35
+ getDataset = async <T>(): Promise<Dataset<T>> => {
37
36
  if (this.dataset) {
38
37
  return this.dataset as Dataset<T>;
39
38
  }
@@ -49,18 +48,18 @@ export class RunTestResult {
49
48
  }
50
49
  const keyValueStoreClient = this.apifyClient.keyValueStore(this.run.defaultKeyValueStoreId);
51
50
  this.keyValueStoreClient = keyValueStoreClient;
52
- return keyValueStoreClient
51
+ return keyValueStoreClient;
53
52
  };
54
53
 
55
- getInput = async<T>(): Promise<T> => {
54
+ getInput = async <T>(): Promise<T> => {
56
55
  if (this.input) {
57
- return this.input as T
56
+ return this.input as T;
58
57
  }
59
58
  const kvs = this.apifyClient.keyValueStore(this.run.defaultKeyValueStoreId);
60
59
  const input = await kvs.getRecord('INPUT');
61
60
  this.input = input?.value;
62
61
  return this.input as T;
63
- }
62
+ };
64
63
 
65
64
  getRunInfo = async (): Promise<ActorRun> => {
66
65
  if (this.runInfo) {
package/lib/types.ts CHANGED
@@ -6,58 +6,57 @@ export type ActorBuild = {
6
6
  actorId: string;
7
7
  buildNumber: string;
8
8
  actorName: string;
9
- }
9
+ };
10
10
 
11
11
  export type RunOptions<T> = {
12
- input: Omit<T, 'actorName'>
13
- options?: ActorCallOptions
14
- prefilledInput?: boolean
12
+ input: Omit<T, 'actorName'>;
13
+ options?: ActorCallOptions;
14
+ prefilledInput?: boolean;
15
15
  /**
16
16
  * If you specify `runId`, all the other options will be ignored and this run's data will
17
17
  * be downloaded instead.
18
18
  *
19
19
  * This is usefull for testing your tests on existing runs
20
20
  */
21
- runId?: string
22
- }
21
+ runId?: string;
22
+ };
23
23
 
24
24
  export type Dataset<T> = {
25
- items: T[]
26
- }
25
+ items: T[];
26
+ };
27
27
 
28
- export type OpenInterval = {
29
- min: number
30
- max?: undefined
31
- } | {
32
- min?: undefined
33
- max: number
34
- }
28
+ export type OpenInterval =
29
+ | {
30
+ min: number;
31
+ max?: undefined;
32
+ }
33
+ | {
34
+ min?: undefined;
35
+ max: number;
36
+ };
35
37
 
36
38
  export type ClosedInterval = {
37
- min: number
38
- max: number
39
- }
39
+ min: number;
40
+ max: number;
41
+ };
40
42
 
41
- export type Interval = number | OpenInterval | ClosedInterval
43
+ export type Interval = number | OpenInterval | ClosedInterval;
42
44
 
43
45
  export type ToFinishWithOptionsWithDefaults = {
44
- status: RunStatus
45
- duration: Interval | null
46
- failedRequests: Interval | null
47
- requestsRetries: Interval | null
48
- forbiddenLogs: string[]
49
- maxRetriesPerRequest: number | null
50
- }
46
+ status: RunStatus;
47
+ duration: Interval | null;
48
+ failedRequests: Interval | null;
49
+ requestsRetries: Interval | null;
50
+ forbiddenLogs: string[];
51
+ maxRetriesPerRequest: number | null;
52
+ };
51
53
  export type IntervalOption<PpeEvent extends string> = keyof Pick<
52
54
  ToFinishWithOptions<PpeEvent>,
53
- | 'datasetItemCount'
54
- | 'requestsRetries'
55
- | 'failedRequests'
56
- | 'duration'
57
- >
55
+ 'datasetItemCount' | 'requestsRetries' | 'failedRequests' | 'duration'
56
+ >;
58
57
 
59
58
  export type ToFinishWithOptions<PpeEvent extends string> = Partial<ToFinishWithOptionsWithDefaults> & {
60
- datasetItemCount: Interval
59
+ datasetItemCount: Interval;
61
60
  /**
62
61
  * Define expected charged (PPE) event counts. You can also define count as `Interval`.
63
62
  *
@@ -72,26 +71,26 @@ export type ToFinishWithOptions<PpeEvent extends string> = Partial<ToFinishWithO
72
71
  * If you omit any PPE event, it's expected count will be 0.
73
72
  * Assertion will fail if `chargedEventCounts` doesn't contain some of the expected events.
74
73
  */
75
- chargedEventCounts?: Record<PpeEvent, Interval>
74
+ chargedEventCounts?: Record<PpeEvent, Interval>;
76
75
  };
77
76
 
78
77
  export type SdkCrawlerStatistics = {
79
- requestsFinished: number
80
- requestsFailed: number
81
- requestsRetries: number
82
- requestsFailedPerMinute: number
83
- requestsFinishedPerMinute: number
84
- requestMinDurationMillis: number
85
- requestMaxDurationMillis: number
86
- requestRetryHistogram: number[]
87
- requestTotalFailedDurationMillis: number
88
- requestTotalFinishedDurationMillis: number
89
- crawlerStartedAt: string
90
- crawlerFinishedAt: string
91
- statsPersistedAt: string
92
- crawlerRuntimeMillis: number
93
- crawlerLastStartTimestamp: number
94
- }
78
+ requestsFinished: number;
79
+ requestsFailed: number;
80
+ requestsRetries: number;
81
+ requestsFailedPerMinute: number;
82
+ requestsFinishedPerMinute: number;
83
+ requestMinDurationMillis: number;
84
+ requestMaxDurationMillis: number;
85
+ requestRetryHistogram: number[];
86
+ requestTotalFailedDurationMillis: number;
87
+ requestTotalFinishedDurationMillis: number;
88
+ crawlerStartedAt: string;
89
+ crawlerFinishedAt: string;
90
+ statsPersistedAt: string;
91
+ crawlerRuntimeMillis: number;
92
+ crawlerLastStartTimestamp: number;
93
+ };
95
94
 
96
95
  export type RunStatus =
97
96
  | 'SUCCEEDED'
@@ -101,22 +100,22 @@ export type RunStatus =
101
100
  | 'ABORTING'
102
101
  | 'ABORTED'
103
102
  | 'TIMING-OUT'
104
- | 'TIMED-OUT'
103
+ | 'TIMED-OUT';
105
104
 
106
105
  export interface ActorMatchers<R = unknown> {
107
- toBeArray: () => R
108
- toBeBoolean: () => R
109
- toBeEmptyArray: () => R
110
- toBeNonEmptyArray: () => R
111
- toBeNonEmptyString: () => R
112
- toBeNumber: () => R
113
- toBeFalse: () => R
114
- toBeTrue: () => R
115
- toBeNonEmptyObject: () => R
116
- toBeObject: () => R
117
- toBeString: () => R
118
- toBeWholeNumber: () => R
119
- toBeWithinRange: (lower: number, upper: number) => R
106
+ toBeArray: () => R;
107
+ toBeBoolean: () => R;
108
+ toBeEmptyArray: () => R;
109
+ toBeNonEmptyArray: () => R;
110
+ toBeNonEmptyString: () => R;
111
+ toBeNumber: () => R;
112
+ toBeFalse: () => R;
113
+ toBeTrue: () => R;
114
+ toBeNonEmptyObject: () => R;
115
+ toBeObject: () => R;
116
+ toBeString: () => R;
117
+ toBeWholeNumber: () => R;
118
+ toBeWithinRange: (lower: number, upper: number) => R;
120
119
  /**
121
120
  * Validates the following properties of a run:
122
121
  * - `status` (default: `SUCCEEDED`)
@@ -128,9 +127,9 @@ export interface ActorMatchers<R = unknown> {
128
127
  * - `datasetItemCount` (required)
129
128
  * - `chargedEventCounts`
130
129
  */
131
- toFinishWith: <PpeEvent extends string>(options: ToFinishWithOptions<PpeEvent>) => Promise<R>
132
- toStartWith: (prefix: string) => R
133
- hard: <T>(actual: T, message?: string) => Assertion
130
+ toFinishWith: <PpeEvent extends string>(options: ToFinishWithOptions<PpeEvent>) => Promise<R>;
131
+ toStartWith: (prefix: string) => R;
132
+ hard: <T>(actual: T, message?: string) => Assertion;
134
133
  }
135
134
 
136
135
  export type ActorTestOptions = Omit<TestOptions, 'retry'> & {
@@ -146,6 +145,8 @@ export type ActorTestOptions = Omit<TestOptions, 'retry'> & {
146
145
 
147
146
  declare module 'vitest' {
148
147
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
- interface Assertion<T = any> extends ActorMatchers<T> { }
150
- interface AsymmetricMatchersContaining extends ActorMatchers { }
148
+ interface Assertion<T = any> extends ActorMatchers<T> {}
149
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
+ interface Matchers<T = any> extends ActorMatchers<T> {}
151
+ interface AsymmetricMatchersContaining extends ActorMatchers {}
151
152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apify-test-tools",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "description": "TBD",
6
6
  "engines": {
@@ -28,6 +28,9 @@
28
28
  "typescript": "^5.7",
29
29
  "vitest": "^3.2.4"
30
30
  },
31
+ "peerDependencies": {
32
+ "vitest": ">=3.2.4"
33
+ },
31
34
  "scripts": {
32
35
  "start": "npm run dist/bin/main.js",
33
36
  "start:dev": "GITHUB_WORKSPACE=local-clone tsx bin/main.ts",
@@ -2,13 +2,10 @@ import process from 'process';
2
2
  import { describe } from 'vitest';
3
3
 
4
4
  import { ApifyClient } from 'apify-client';
5
- import { testStandbyActor, testTestActor } from '../../lib/lib';
6
- import { RunTestResult } from '../../lib/run-test-result';
5
+ import { testStandbyActor, testTestActor } from '../../lib/lib.js';
6
+ import { RunTestResult } from '../../lib/run-test-result.js';
7
7
 
8
- type PpeEventType =
9
- | 'actor-start'
10
- | 'search-page-scraped'
11
- | 'ads-scraped'
8
+ type PpeEventType = 'actor-start' | 'search-page-scraped' | 'ads-scraped';
12
9
  enum PpeEventEnum {
13
10
  ACTOR_START = 'actor-start',
14
11
  SEARCH_PAGE_SCRAPED = 'search-page-scraped',
@@ -45,7 +42,7 @@ describe('custom-matchers', { timeout: 100_000 }, () => {
45
42
  },
46
43
  });
47
44
 
48
- await expect(runResult).toFinishWith<typeof PPE_EVENT_CONST[keyof typeof PPE_EVENT_CONST]>({
45
+ await expect(runResult).toFinishWith<(typeof PPE_EVENT_CONST)[keyof typeof PPE_EVENT_CONST]>({
49
46
  datasetItemCount: { min: 1, max: 20 },
50
47
  chargedEventCounts: {
51
48
  'actor-start': 1,
@@ -67,7 +64,7 @@ describe('custom-matchers', { timeout: 100_000 }, () => {
67
64
  maxRequests: 3,
68
65
  aggregateContacts: true,
69
66
  },
70
- })
67
+ });
71
68
  expect(status).toBe(200);
72
69
  expect(data[0].domain).toEqual('apify.com');
73
70
  }
@@ -78,7 +75,7 @@ describe('custom-matchers', { timeout: 100_000 }, () => {
78
75
  maxRequests: 3,
79
76
  aggregateContacts: true,
80
77
  },
81
- })
78
+ });
82
79
  expect(status).toBe(200);
83
80
  expect(data[0].domain).toEqual('apify.com');
84
81
  });