Skip to content

Commit 7964db7

Browse files
committed
chore: improve TS typing in tests
1 parent 8a32faa commit 7964db7

40 files changed

+390
-287
lines changed

package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"@rsbuild/plugin-node-polyfill": "^1.3.0",
8888
"@stylistic/eslint-plugin-ts": "^4.2.0",
8989
"@types/async-retry": "^1.4.5",
90+
"@types/compression": "^1.8.1",
9091
"@types/content-type": "^1.1.5",
9192
"@types/express": "^5.0.0",
9293
"@types/fs-extra": "^11.0.1",

test/_helper.ts

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@ import { launchPuppeteer, puppeteerUtils } from '@crawlee/puppeteer';
22
import { expect } from 'vitest';
33

44
import { mockServer } from './mock_server/server';
5+
import { Dictionary } from 'apify-client';
6+
import { Browser as PuppeteerBrowser } from 'puppeteer';
7+
import { Request } from 'express';
58

69
export class Browser {
10+
private browser: PuppeteerBrowser | undefined;
11+
712
async start() {
813
this.browser = await launchPuppeteer({
914
launchOptions: { headless: true, args: ['--disable-web-security', '--no-sandbox'] },
1015
});
1116
return this.browser;
1217
}
1318

14-
async getInjectedPage(baseUrl, DEFAULT_OPTIONS, gotoUrl = null) {
19+
async getInjectedPage(baseUrl?: string, DEFAULT_OPTIONS?: Dictionary<any>, gotoUrl?: string) {
20+
if (!this.browser) throw new Error('Browser is not started. Call start() first.');
21+
1522
const page = await this.browser.newPage();
1623
if (gotoUrl) await page.goto(gotoUrl);
1724

@@ -20,7 +27,7 @@ export class Browser {
2027
page.on('console', (msg) => console.log(msg.text()));
2128
await page.evaluate(
2229
(url, defaultQuery) => {
23-
window.client = new window.Apify.ApifyClient({
30+
(window as any).client = new (window as any).Apify.ApifyClient({
2431
baseUrl: url,
2532
maxRetries: 0,
2633
...defaultQuery,
@@ -34,7 +41,7 @@ export class Browser {
3441
}
3542

3643
async cleanUpBrowser() {
37-
return this.browser.close.call(this.browser);
44+
return this.browser?.close.call(this.browser);
3845
}
3946
}
4047

@@ -49,9 +56,9 @@ const getExpectedQuery = (callQuery = {}) => {
4956
};
5057
};
5158

52-
function optsToQuery(params) {
59+
function optsToQuery(params: Dictionary<any>) {
5360
return Object.entries(params)
54-
.filter(([k, v]) => v !== false)
61+
.filter(([_, v]) => v !== false)
5562
.map(([k, v]) => {
5663
if (v === true) v = '1';
5764
else if (Array.isArray(v)) v = v.join(',');
@@ -61,24 +68,46 @@ function optsToQuery(params) {
6168
.reduce((newObj, [k, v]) => {
6269
newObj[k] = v;
6370
return newObj;
64-
}, {});
71+
}, {} as Dictionary<string>);
6572
}
6673

67-
export const validateRequest = (query = {}, params = {}, body = {}, additionalHeaders = {}) => {
74+
export const validateRequest = ({
75+
query = {},
76+
params = {},
77+
body = {},
78+
additionalHeaders = {},
79+
path,
80+
}: {
81+
query?: Request['query'];
82+
params?: Request['params'];
83+
body?: Request['body'];
84+
additionalHeaders?: Request['headers'];
85+
path?: Request['path'];
86+
} = {}) => {
6887
const headers = {
6988
authorization: `Bearer ${DEFAULT_OPTIONS.token}`,
7089
...additionalHeaders,
7190
};
7291
const request = mockServer.getLastRequest();
92+
if (path) {
93+
expect(request?.path).toEqual(path);
94+
}
95+
7396
const expectedQuery = getExpectedQuery(query);
74-
if (query !== false) expect(request.query).toEqual(expectedQuery);
75-
if (params !== false) expect(request.params).toEqual(params);
76-
if (body !== false) expect(request.body).toEqual(body);
97+
if (query) expect(request?.query).toEqual(expectedQuery);
98+
if (params) expect(request?.params).toEqual(params);
99+
if (body) expect(request?.body).toEqual(body);
77100
Object.entries(headers).forEach(([key, value]) => {
78101
// Browsers tend to send headers "a bit differently".
79-
expect(request.headers).toHaveProperty(key);
80-
const expectedHeaderValue = value.toLowerCase().replace(/\s/g, '');
81-
const actualHeaderValue = request.headers[key].toLowerCase().replace(/\s/g, '');
102+
expect(request?.headers).toHaveProperty(key);
103+
const expectedHeaderValue = Array.isArray(value)
104+
? value.join('').toLowerCase().replace(/\s/g, '')
105+
: value.toLowerCase().replace(/\s/g, '');
106+
107+
const actualHeaderValue = Array.isArray(request?.headers[key])
108+
? request?.headers[key].join('').toLowerCase().replace(/\s/g, '')
109+
: request?.headers[key]?.toLowerCase().replace(/\s/g, '');
110+
82111
expect(actualHeaderValue).toBe(expectedHeaderValue);
83112
});
84113
};

test/actors.test.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { setTimeout } from 'node:timers/promises';
33
import c from 'ansi-colors';
44
import { ActorListSortBy, ApifyClient, LoggerActorRedirect } from 'apify-client';
55
import express from 'express';
6+
import type { Page } from 'puppeteer';
7+
import type { AddressInfo } from 'net';
68
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect,test, vi } from 'vitest';
79

810
import { LEVELS,Log } from '@apify/log';
@@ -11,23 +13,24 @@ import { stringifyWebhooksToBase64 } from '../src/utils';
1113
import { Browser, DEFAULT_OPTIONS,validateRequest } from './_helper';
1214
import { createDefaultApp,mockServer } from './mock_server/server';
1315
import { MOCKED_ACTOR_LOGS_PROCESSED, StatusGenerator } from './mock_server/test_utils';
16+
import { WEBHOOK_EVENT_TYPES } from '@apify/consts';
1417

1518
describe('Actor methods', () => {
16-
let baseUrl;
19+
let baseUrl: string;
1720
const browser = new Browser();
1821

1922
beforeAll(async () => {
20-
const server = await mockServer.start();
23+
const server = await mockServer.start() as import('http').Server;
2124
await browser.start();
22-
baseUrl = `http://localhost:${server.address().port}`;
25+
baseUrl = `http://localhost:${(server.address() as AddressInfo).port}`;
2326
});
2427

2528
afterAll(async () => {
2629
await Promise.all([mockServer.close(), browser.cleanUpBrowser()]);
2730
});
2831

29-
let client;
30-
let page;
32+
let client: ApifyClient;
33+
let page: Page;
3134
beforeEach(async () => {
3235
page = await browser.getInjectedPage(baseUrl, DEFAULT_OPTIONS);
3336
client = new ApifyClient({
@@ -37,7 +40,8 @@ describe('Actor methods', () => {
3740
});
3841
});
3942
afterEach(async () => {
40-
client = null;
43+
// purge the client instance to avoid sharing state between tests
44+
client = null as any;
4145
page.close().catch(() => {});
4246
});
4347

@@ -90,7 +94,7 @@ describe('Actor methods', () => {
9094
const actorId = 'some-id';
9195

9296
const res = await client.actor(actorId).get();
93-
expect(res.id).toEqual('get-actor');
97+
expect(res?.id).toEqual('get-actor');
9498
validateRequest({}, { actorId });
9599

96100
const browserRes = await page.evaluate((id) => client.actor(id).get(), actorId);
@@ -115,7 +119,7 @@ describe('Actor methods', () => {
115119

116120
const defaultBuildClient = await client.actor(actorId).defaultBuild();
117121
const res = await defaultBuildClient.get();
118-
expect(res.id).toEqual('get-build');
122+
expect(res?.id).toEqual('get-build');
119123
validateRequest({}, { buildId: 'default-build-get' });
120124

121125
const browserRes = await page.evaluate(async (id) => {
@@ -179,7 +183,7 @@ describe('Actor methods', () => {
179183
const actorId = 'some-id';
180184
const input = {
181185
foo: 'bar',
182-
fn: async (a, b) => a + b,
186+
fn: async (a: number, b: number) => a + b,
183187
};
184188

185189
const expectedRequestProps = [
@@ -196,7 +200,7 @@ describe('Actor methods', () => {
196200
const browserRes = await page.evaluate((id) => {
197201
return client.actor(id).start({
198202
foo: 'bar',
199-
fn: async (a, b) => a + b,
203+
fn: async (a: number, b: number) => a + b,
200204
});
201205
}, actorId);
202206
expect(browserRes).toEqual(res);
@@ -207,11 +211,11 @@ describe('Actor methods', () => {
207211
const actorId = 'some-id';
208212
const webhooks = [
209213
{
210-
eventTypes: ['ACTOR.RUN.CREATED'],
214+
eventTypes: [WEBHOOK_EVENT_TYPES.ACTOR_RUN_CREATED],
211215
requestUrl: 'https://example.com/run-created',
212216
},
213217
{
214-
eventTypes: ['ACTOR.RUN.SUCCEEDED'],
218+
eventTypes: [WEBHOOK_EVENT_TYPES.ACTOR_RUN_SUCCEEDED],
215219
requestUrl: 'https://example.com/run-succeeded',
216220
},
217221
];
@@ -357,7 +361,7 @@ describe('Actor methods', () => {
357361
});
358362

359363
describe('lastRun()', () => {
360-
test.each(['get', 'dataset', 'keyValueStore', 'requestQueue', 'log'])('%s() works', async (method) => {
364+
test.each(['get', 'dataset', 'keyValueStore', 'requestQueue', 'log'] as const)('%s() works', async (method) => {
361365
const actorId = 'some-actor-id';
362366
const requestedStatus = 'SUCCEEDED';
363367

@@ -367,7 +371,7 @@ describe('Actor methods', () => {
367371
if (method === 'log') {
368372
expect(res).toEqual('last-run-log');
369373
} else {
370-
expect(res.id).toEqual(`last-run-${method}`);
374+
expect(res?.id).toEqual(`last-run-${method}`);
371375
}
372376
validateRequest({ status: requestedStatus }, { actorId });
373377

@@ -581,7 +585,7 @@ describe('Actor methods', () => {
581585
const envVarName = 'TEST_ENV_VAR';
582586

583587
const res = await client.actor(actorId).version(versionNumber).envVar(envVarName).get();
584-
expect(res.id).toEqual('get-actor-env-var');
588+
expect(res?.id).toEqual('get-actor-env-var');
585589
validateRequest({}, { actorId, versionNumber, envVarName });
586590

587591
const browserRes = await page.evaluate(
@@ -681,8 +685,8 @@ describe('Actor methods', () => {
681685
});
682686

683687
describe('Run actor with redirected logs', () => {
684-
let baseUrl;
685-
let client;
688+
let baseUrl: string;
689+
let client: ApifyClient;
686690
const statusGenerator = new StatusGenerator();
687691

688692
beforeAll(async () => {
@@ -698,7 +702,7 @@ describe('Run actor with redirected logs', () => {
698702
});
699703
const app = createDefaultApp(router);
700704
const server = await mockServer.start(undefined, app);
701-
baseUrl = `http://localhost:${server.address().port}`;
705+
baseUrl = `http://localhost:${(server.address() as AddressInfo).port}`;
702706
});
703707

704708
afterAll(async () => {
@@ -715,7 +719,7 @@ describe('Run actor with redirected logs', () => {
715719
afterEach(async () => {
716720
// Reset the generator to so that the next test starts fresh
717721
statusGenerator.reset();
718-
client = null;
722+
client = null as unknown as ApifyClient;
719723
});
720724

721725
const testCases = [
@@ -727,7 +731,7 @@ describe('Run actor with redirected logs', () => {
727731
log: new Log({ level: LEVELS.DEBUG, prefix: 'custom prefix...', logger: new LoggerActorRedirect() }),
728732
},
729733
},
730-
];
734+
] as const;
731735

732736
describe('actor.call - redirected logs', () => {
733737
test.each(testCases)('logOptions:$logOptions', async ({ expectedPrefix, logOptions }) => {

test/apify_api_error.test.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import { ApifyClient } from 'apify-client';
2-
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect,test, vi } from 'vitest';
1+
import { ApifyApiError, ApifyClient, Dictionary } from 'apify-client';
2+
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
33

44
import { Browser, DEFAULT_OPTIONS } from './_helper';
55
import { mockServer } from './mock_server/server';
6+
import { AddressInfo } from 'node:net';
67

78
describe('ApifyApiError', () => {
8-
let baseUrl;
9+
let baseUrl: string;
910
const browser = new Browser();
1011

1112
beforeAll(async () => {
1213
await browser.start();
1314
const server = await mockServer.start();
14-
baseUrl = `http://localhost:${server.address().port}`;
15+
baseUrl = `http://localhost:${(server.address() as AddressInfo).port}`;
1516
});
1617

1718
afterAll(async () => {
@@ -25,7 +26,9 @@ describe('ApifyApiError', () => {
2526
try {
2627
await actorCollectionClient[method]();
2728
throw new Error('wrong error');
28-
} catch (err) {
29+
} catch (err: any) {
30+
if (!(err instanceof ApifyApiError)) throw err;
31+
2932
expect(err.name).toEqual('ApifyApiError');
3033
// This does not work in v10 and lower, but we want to be able to run tests for v10,
3134
// because some people might still use it. They will just see clientMethod: undefined.
@@ -45,20 +48,22 @@ describe('ApifyApiError', () => {
4548
const page = await browser.getInjectedPage();
4649
const method = 'list';
4750
const error = await page.evaluate(async (m) => {
48-
const client = new window.Apify.ApifyClient();
51+
const client = new (window as any).Apify.ApifyClient();
4952
const actorCollectionClient = client.actors();
5053
try {
5154
await actorCollectionClient[m]();
5255
throw new Error('wrong error');
53-
} catch (err) {
54-
const serializableErr = {};
56+
} catch (err: any) {
57+
const serializableErr: Dictionary<any> = {};
5558
Object.getOwnPropertyNames(err).forEach((prop) => {
5659
serializableErr[prop] = err[prop];
5760
});
5861
serializableErr.resourcePath = actorCollectionClient.resourcePath;
59-
return serializableErr;
62+
return serializableErr as Dictionary<any>;
6063
}
6164
}, method);
65+
66+
6267
expect(error.name).toEqual('ApifyApiError');
6368
expect(error.clientMethod).toBe(`ActorCollectionClient.${method}`);
6469
expect(error.type).toEqual('token-not-provided');
@@ -84,6 +89,8 @@ describe('ApifyApiError', () => {
8489
await client.dataset(datasetId).pushItems(data);
8590
throw new Error('wrong error');
8691
} catch (err) {
92+
if (!(err instanceof ApifyApiError)) throw err;
93+
8794
expect(err.name).toEqual('ApifyApiError');
8895
expect(err.type).toEqual('schema-validation-error');
8996
expect(err.data).toEqual({
@@ -97,13 +104,13 @@ describe('ApifyApiError', () => {
97104
const page = await browser.getInjectedPage();
98105
const error = await page.evaluate(
99106
async (cConfig, dId, d) => {
100-
const client = new window.Apify.ApifyClient(cConfig);
107+
const client = new (window as any).Apify.ApifyClient(cConfig);
101108
const datasetClient = client.dataset(dId);
102109
try {
103110
await datasetClient.pushItems(d);
104111
throw new Error('wrong error');
105-
} catch (err) {
106-
const serializableErr = {};
112+
} catch (err: any) {
113+
const serializableErr: Dictionary<any> = {};
107114
Object.getOwnPropertyNames(err).forEach((prop) => {
108115
serializableErr[prop] = err[prop];
109116
});

0 commit comments

Comments
 (0)