thenavisapp
Version:
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
401 lines (320 loc) • 9.52 kB
text/typescript
import {
Agent as HttpAgent,
request as httpRequest,
type IncomingMessage,
type RequestOptions,
} from 'node:http';
import test from 'ava';
import is from '@sindresorhus/is';
import type {Handler} from 'express';
import delay from 'delay';
import got, {
Options,
type BeforeRequestHook,
type Headers,
type Hooks,
type OptionsInit,
type RequestFunction,
} from '../source/index.js';
import withServer from './helpers/with-server.js';
const echoHeaders: Handler = (request, response) => {
request.resume();
response.end(JSON.stringify(request.headers));
};
test('preserves global defaults', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const globalHeaders = await got('').json();
const instanceHeaders = await got.extend()('').json();
t.deepEqual(instanceHeaders, globalHeaders);
});
test('supports instance defaults', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
headers: {
'user-agent': 'custom-ua-string',
},
});
const headers = await instance('').json<Headers>();
t.is(headers['user-agent'], 'custom-ua-string');
});
test('supports invocation overrides', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
headers: {
'user-agent': 'custom-ua-string',
},
});
const headers = await instance({
headers: {
'user-agent': 'different-ua-string',
},
}).json<Headers>();
t.is(headers['user-agent'], 'different-ua-string');
});
test('carries previous instance defaults', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instanceA = got.extend({
headers: {
'x-foo': 'foo',
},
});
const instanceB = instanceA.extend({
headers: {
'x-bar': 'bar',
},
});
const headers = await instanceB('').json<Headers>();
t.is(headers['x-foo'], 'foo');
t.is(headers['x-bar'], 'bar');
});
test('custom headers (extend)', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const options = {headers: {unicorn: 'rainbow'}};
const instance = got.extend(options);
const headers = await instance('').json<Headers>();
t.is(headers.unicorn, 'rainbow');
});
test('extend overwrites arrays with a deep clone', t => {
const x = () => {};
const y = () => {};
const beforeRequest = [x];
const a = got.extend({hooks: {beforeRequest} as unknown as Hooks});
beforeRequest[0] = y;
t.deepEqual(a.defaults.options.hooks.beforeRequest, [x] as unknown as BeforeRequestHook[]);
t.not(a.defaults.options.hooks.beforeRequest, beforeRequest as unknown as BeforeRequestHook[]);
});
test('hooks are merged on got.extend()', t => {
const hooksA = [() => {}];
const hooksB = [() => {}];
const instanceA = got.extend({hooks: {beforeRequest: hooksA}});
const extended = instanceA.extend({hooks: {beforeRequest: hooksB}});
t.deepEqual(extended.defaults.options.hooks.beforeRequest, [...hooksA, ...hooksB]);
});
test('custom endpoint with custom headers (extend)', withServer, async (t, server) => {
server.all('/', echoHeaders);
const instance = got.extend({headers: {unicorn: 'rainbow'}, prefixUrl: server.url});
const headers = await instance('').json<Headers>();
t.is(headers.unicorn, 'rainbow');
t.not(headers['user-agent'], undefined);
});
test('no tampering with defaults', t => {
t.throws(() => {
got.defaults.options.prefixUrl = 'http://google.com';
});
t.is(got.defaults.options.prefixUrl, '');
});
test('can set defaults to `new Options(...)`', t => {
const instance = got.extend({
mutableDefaults: true,
followRedirect: false,
});
t.notThrows(() => {
instance.defaults.options = new Options({
followRedirect: false,
}, undefined, instance.defaults.options);
});
t.false(instance.defaults.options.followRedirect);
t.notThrows(() => {
instance.defaults.options = new Options({});
});
t.true(instance.defaults.options.followRedirect);
});
test('can set mutable defaults using got.extend', t => {
const instance = got.extend({
mutableDefaults: true,
followRedirect: false,
});
t.notThrows(() => {
instance.defaults.options.followRedirect = true;
});
t.true(instance.defaults.options.followRedirect);
});
test('only plain objects are freezed', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
agent: {
http: new HttpAgent({keepAlive: true}),
},
mutableDefaults: true,
});
t.notThrows(() => {
(instance.defaults.options.agent as any).http.keepAlive = true;
});
});
// eslint-disable-next-line ava/no-skip-test
test.skip('defaults are cloned on instance creation', t => {
const options: OptionsInit = {hooks: {beforeRequest: [() => {}]}};
const instance = got.extend(options);
const context = {
foo: {},
};
t.notThrows(() => {
options.context = context;
delete options.hooks!.beforeRequest![0];
});
t.not(options.context!.foo, instance.defaults.options.context.foo);
t.not(options.hooks!.beforeRequest, instance.defaults.options.hooks.beforeRequest);
});
test('ability to pass a custom request method', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
let isCalled = false;
const request: RequestFunction = (...args: [
string | URL | RequestOptions,
(RequestOptions | ((response: IncomingMessage) => void))?,
((response: IncomingMessage) => void)?,
]) => {
isCalled = true;
// @ts-expect-error Overload error
return httpRequest(...args);
};
const instance = got.extend({request});
await instance('');
t.true(isCalled);
});
test('does not include the `request` option in normalized `http` options', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
let isCalled = false;
const request: RequestFunction = (...args: [
string | URL | RequestOptions,
(RequestOptions | ((response: IncomingMessage) => void))?,
((response: IncomingMessage) => void)?,
]) => {
isCalled = true;
t.false(Reflect.has(args[0] as RequestOptions, 'request'));
// @ts-expect-error Overload error
return httpRequest(...args);
};
const instance = got.extend({request});
await instance('');
t.true(isCalled);
});
test('should pass an options object into an initialization hook after .extend', withServer, async (t, server, got) => {
t.plan(1);
server.get('/', echoHeaders);
let first = true;
const instance = got.extend({
hooks: {
init: [
options => {
if (!first) {
t.deepEqual(options, {});
}
first = false;
},
],
},
});
await instance('', {});
});
test('hooks aren\'t overriden when merging options', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
let isCalled = false;
const instance = got.extend({
hooks: {
beforeRequest: [
() => {
isCalled = true;
},
],
},
});
await instance({});
t.true(isCalled);
});
test('extend with custom handlers', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
handlers: [
(options, next) => {
options.headers.unicorn = 'rainbow';
return next(options);
},
],
});
const headers = await instance('').json<Headers>();
t.is(headers.unicorn, 'rainbow');
});
test('extend with instances', t => {
const a = got.extend({prefixUrl: new URL('https://example.com/')});
const b = got.extend(a);
t.is(b.defaults.options.prefixUrl.toString(), 'https://example.com/');
});
test('extend with a chain', t => {
const a = got.extend({prefixUrl: 'https://example.com/'});
const b = got.extend(a, {headers: {foo: 'bar'}});
t.is(b.defaults.options.prefixUrl.toString(), 'https://example.com/');
t.is(b.defaults.options.headers.foo, 'bar');
});
test('async handlers', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
handlers: [
(options, next) => {
if (options.isStream) {
return next(options);
}
return (async () => {
const result = await next(options);
// @ts-expect-error Manual tests
result.modified = true;
return result;
})();
},
],
});
const promise = instance('');
t.true(is.function_(promise.cancel));
// @ts-expect-error Manual tests
t.true((await promise).modified);
});
test('async handlers can throw', async t => {
const message = 'meh';
const instance = got.extend({
handlers: [
async () => {
throw new Error(message);
},
],
});
await t.throwsAsync(instance('https://example.com'), {
instanceOf: Error,
message,
});
});
test('setting dnsCache to true points to global cache', t => {
const a = got.extend({
dnsCache: true,
});
const b = got.extend({
dnsCache: true,
});
t.is(a.defaults.options.dnsCache, b.defaults.options.dnsCache);
});
test('waits for handlers to finish', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
handlers: [
async (options, next) => {
await delay(1000);
return next(options);
},
async (options, next) => {
options.headers.foo = 'bar';
return next(options);
},
],
});
const {foo} = await instance('').json<{foo: 'bar'}>();
t.is(foo, 'bar');
});
test('does not append to internal _init on new requests', withServer, async (t, server, got) => {
server.get('/', echoHeaders);
const instance = got.extend({
mutableDefaults: true,
});
const {length} = (instance.defaults.options as any)._init;
await got('', {
context: {},
});
t.is((instance.defaults.options as any)._init.length, length);
});