mirror of
https://github.com/tenrok/axios.git
synced 2026-05-15 11:59:42 +03:00
feat(fetch): add fetch, Request, Response env config variables for the adapter; (#7003)
* feat(fetch): add fetch, Request, Response env config variables for the adapter; * feat(fetch): fixed design issue for environments without fetch API globals;
This commit is contained in:
@@ -423,6 +423,9 @@ declare namespace axios {
|
||||
insecureHTTPParser?: boolean;
|
||||
env?: {
|
||||
FormData?: new (...args: any[]) => object;
|
||||
fetch?: (input: URL | Request | string, init?: RequestInit) => Promise<Response>;
|
||||
Request?: new (input: (RequestInfo | URL), init?: RequestInit) => Request;
|
||||
Response?: new (body?: (BodyInit | null), init?: ResponseInit) => Response;
|
||||
};
|
||||
formSerializer?: FormSerializerOptions;
|
||||
family?: AddressFamily;
|
||||
|
||||
Vendored
+3
@@ -355,6 +355,9 @@ export interface AxiosRequestConfig<D = any> {
|
||||
insecureHTTPParser?: boolean;
|
||||
env?: {
|
||||
FormData?: new (...args: any[]) => object;
|
||||
fetch?: (input: URL | Request | string, init?: RequestInit) => Promise<Response>;
|
||||
Request?: new (input: (RequestInfo | URL), init?: RequestInit) => Request;
|
||||
Response?: new (body?: (BodyInit | null), init?: ResponseInit) => Response;
|
||||
};
|
||||
formSerializer?: FormSerializerOptions;
|
||||
family?: AddressFamily;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import utils from '../utils.js';
|
||||
import httpAdapter from './http.js';
|
||||
import xhrAdapter from './xhr.js';
|
||||
import fetchAdapter from './fetch.js';
|
||||
import * as fetchAdapter from './fetch.js';
|
||||
import AxiosError from "../core/AxiosError.js";
|
||||
|
||||
const knownAdapters = {
|
||||
http: httpAdapter,
|
||||
xhr: xhrAdapter,
|
||||
fetch: fetchAdapter
|
||||
fetch: {
|
||||
get: fetchAdapter.getFetch,
|
||||
}
|
||||
}
|
||||
|
||||
utils.forEach(knownAdapters, (fn, value) => {
|
||||
@@ -26,7 +28,7 @@ const renderReason = (reason) => `- ${reason}`;
|
||||
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
|
||||
|
||||
export default {
|
||||
getAdapter: (adapters) => {
|
||||
getAdapter: (adapters, config) => {
|
||||
adapters = utils.isArray(adapters) ? adapters : [adapters];
|
||||
|
||||
const {length} = adapters;
|
||||
@@ -49,7 +51,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter) {
|
||||
if (adapter && (utils.isFunction(adapter) || (adapter = adapter.get(config)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+237
-180
@@ -8,14 +8,18 @@ import {progressEventReducer, progressEventDecorator, asyncDecorator} from "../h
|
||||
import resolveConfig from "../helpers/resolveConfig.js";
|
||||
import settle from "../core/settle.js";
|
||||
|
||||
const isFetchSupported = typeof fetch === 'function' && typeof Request === 'function' && typeof Response === 'function';
|
||||
const isReadableStreamSupported = isFetchSupported && typeof ReadableStream === 'function';
|
||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
const {isFunction} = utils;
|
||||
|
||||
const globalFetchAPI = (({fetch, Request, Response}) => ({
|
||||
fetch, Request, Response
|
||||
}))(utils.global);
|
||||
|
||||
const {
|
||||
ReadableStream, TextEncoder
|
||||
} = utils.global;
|
||||
|
||||
// used only inside the fetch adapter
|
||||
const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
|
||||
((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
|
||||
async (str) => new Uint8Array(await new Response(str).arrayBuffer())
|
||||
);
|
||||
|
||||
const test = (fn, ...args) => {
|
||||
try {
|
||||
@@ -25,205 +29,258 @@ const test = (fn, ...args) => {
|
||||
}
|
||||
}
|
||||
|
||||
const supportsRequestStream = isReadableStreamSupported && test(() => {
|
||||
let duplexAccessed = false;
|
||||
const factory = (env) => {
|
||||
const {fetch, Request, Response} = Object.assign({}, globalFetchAPI, env);
|
||||
const isFetchSupported = isFunction(fetch);
|
||||
const isRequestSupported = isFunction(Request);
|
||||
const isResponseSupported = isFunction(Response);
|
||||
|
||||
const hasContentType = new Request(platform.origin, {
|
||||
body: new ReadableStream(),
|
||||
method: 'POST',
|
||||
get duplex() {
|
||||
duplexAccessed = true;
|
||||
return 'half';
|
||||
},
|
||||
}).headers.has('Content-Type');
|
||||
if (!isFetchSupported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return duplexAccessed && !hasContentType;
|
||||
});
|
||||
const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream);
|
||||
|
||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
|
||||
((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
|
||||
async (str) => new Uint8Array(await new Request(str).arrayBuffer())
|
||||
);
|
||||
|
||||
const supportsResponseStream = isReadableStreamSupported &&
|
||||
test(() => utils.isReadableStream(new Response('').body));
|
||||
const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
|
||||
let duplexAccessed = false;
|
||||
|
||||
const hasContentType = new Request(platform.origin, {
|
||||
body: new ReadableStream(),
|
||||
method: 'POST',
|
||||
get duplex() {
|
||||
duplexAccessed = true;
|
||||
return 'half';
|
||||
},
|
||||
}).headers.has('Content-Type');
|
||||
|
||||
const resolvers = {
|
||||
stream: supportsResponseStream && ((res) => res.body)
|
||||
};
|
||||
return duplexAccessed && !hasContentType;
|
||||
});
|
||||
|
||||
const supportsResponseStream = isResponseSupported && isReadableStreamSupported &&
|
||||
test(() => utils.isReadableStream(new Response('').body));
|
||||
|
||||
const resolvers = {
|
||||
stream: supportsResponseStream && ((res) => res.body)
|
||||
};
|
||||
|
||||
isFetchSupported && ((() => {
|
||||
['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
|
||||
!resolvers[type] && (resolvers[type] = (res, config) => {
|
||||
let method = res && res[type];
|
||||
|
||||
if (method) {
|
||||
return method.call(res);
|
||||
}
|
||||
|
||||
isFetchSupported && (((res) => {
|
||||
['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
|
||||
!resolvers[type] && (resolvers[type] = utils.isFunction(res[type]) ? (res) => res[type]() :
|
||||
(_, config) => {
|
||||
throw new AxiosError(`Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
|
||||
})
|
||||
});
|
||||
})(new Response));
|
||||
|
||||
const getBodyLength = async (body) => {
|
||||
if (body == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(utils.isBlob(body)) {
|
||||
return body.size;
|
||||
}
|
||||
|
||||
if(utils.isSpecCompliantForm(body)) {
|
||||
const _request = new Request(platform.origin, {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
return (await _request.arrayBuffer()).byteLength;
|
||||
}
|
||||
})());
|
||||
|
||||
if(utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
||||
return body.byteLength;
|
||||
}
|
||||
const getBodyLength = async (body) => {
|
||||
if (body == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(utils.isURLSearchParams(body)) {
|
||||
body = body + '';
|
||||
}
|
||||
if (utils.isBlob(body)) {
|
||||
return body.size;
|
||||
}
|
||||
|
||||
if(utils.isString(body)) {
|
||||
return (await encodeText(body)).byteLength;
|
||||
}
|
||||
}
|
||||
|
||||
const resolveBodyLength = async (headers, body) => {
|
||||
const length = utils.toFiniteNumber(headers.getContentLength());
|
||||
|
||||
return length == null ? getBodyLength(body) : length;
|
||||
}
|
||||
|
||||
export default isFetchSupported && (async (config) => {
|
||||
let {
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
signal,
|
||||
cancelToken,
|
||||
timeout,
|
||||
onDownloadProgress,
|
||||
onUploadProgress,
|
||||
responseType,
|
||||
headers,
|
||||
withCredentials = 'same-origin',
|
||||
fetchOptions
|
||||
} = resolveConfig(config);
|
||||
|
||||
responseType = responseType ? (responseType + '').toLowerCase() : 'text';
|
||||
|
||||
let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
|
||||
|
||||
let request;
|
||||
|
||||
const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
|
||||
composedSignal.unsubscribe();
|
||||
});
|
||||
|
||||
let requestContentLength;
|
||||
|
||||
try {
|
||||
if (
|
||||
onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
|
||||
(requestContentLength = await resolveBodyLength(headers, data)) !== 0
|
||||
) {
|
||||
let _request = new Request(url, {
|
||||
if (utils.isSpecCompliantForm(body)) {
|
||||
const _request = new Request(platform.origin, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
duplex: "half"
|
||||
body,
|
||||
});
|
||||
|
||||
let contentTypeHeader;
|
||||
|
||||
if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
|
||||
headers.setContentType(contentTypeHeader)
|
||||
}
|
||||
|
||||
if (_request.body) {
|
||||
const [onProgress, flush] = progressEventDecorator(
|
||||
requestContentLength,
|
||||
progressEventReducer(asyncDecorator(onUploadProgress))
|
||||
);
|
||||
|
||||
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
|
||||
}
|
||||
return (await _request.arrayBuffer()).byteLength;
|
||||
}
|
||||
|
||||
if (!utils.isString(withCredentials)) {
|
||||
withCredentials = withCredentials ? 'include' : 'omit';
|
||||
if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
||||
return body.byteLength;
|
||||
}
|
||||
|
||||
// Cloudflare Workers throws when credentials are defined
|
||||
// see https://github.com/cloudflare/workerd/issues/902
|
||||
const isCredentialsSupported = "credentials" in Request.prototype;
|
||||
request = new Request(url, {
|
||||
...fetchOptions,
|
||||
signal: composedSignal,
|
||||
method: method.toUpperCase(),
|
||||
headers: headers.normalize().toJSON(),
|
||||
body: data,
|
||||
duplex: "half",
|
||||
credentials: isCredentialsSupported ? withCredentials : undefined
|
||||
if (utils.isURLSearchParams(body)) {
|
||||
body = body + '';
|
||||
}
|
||||
|
||||
if (utils.isString(body)) {
|
||||
return (await encodeText(body)).byteLength;
|
||||
}
|
||||
}
|
||||
|
||||
const resolveBodyLength = async (headers, body) => {
|
||||
const length = utils.toFiniteNumber(headers.getContentLength());
|
||||
|
||||
return length == null ? getBodyLength(body) : length;
|
||||
}
|
||||
|
||||
return async (config) => {
|
||||
let {
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
signal,
|
||||
cancelToken,
|
||||
timeout,
|
||||
onDownloadProgress,
|
||||
onUploadProgress,
|
||||
responseType,
|
||||
headers,
|
||||
withCredentials = 'same-origin',
|
||||
fetchOptions
|
||||
} = resolveConfig(config);
|
||||
|
||||
responseType = responseType ? (responseType + '').toLowerCase() : 'text';
|
||||
|
||||
let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
|
||||
|
||||
let request = null;
|
||||
|
||||
const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
|
||||
composedSignal.unsubscribe();
|
||||
});
|
||||
|
||||
let response = await fetch(request, fetchOptions);
|
||||
let requestContentLength;
|
||||
|
||||
const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
|
||||
try {
|
||||
if (
|
||||
onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
|
||||
(requestContentLength = await resolveBodyLength(headers, data)) !== 0
|
||||
) {
|
||||
let _request = new Request(url, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
duplex: "half"
|
||||
});
|
||||
|
||||
if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
|
||||
const options = {};
|
||||
let contentTypeHeader;
|
||||
|
||||
['status', 'statusText', 'headers'].forEach(prop => {
|
||||
options[prop] = response[prop];
|
||||
});
|
||||
|
||||
const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
|
||||
|
||||
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
|
||||
responseContentLength,
|
||||
progressEventReducer(asyncDecorator(onDownloadProgress), true)
|
||||
) || [];
|
||||
|
||||
response = new Response(
|
||||
trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
|
||||
flush && flush();
|
||||
unsubscribe && unsubscribe();
|
||||
}),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
responseType = responseType || 'text';
|
||||
|
||||
let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](response, config);
|
||||
|
||||
!isStreamResponse && unsubscribe && unsubscribe();
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
settle(resolve, reject, {
|
||||
data: responseData,
|
||||
headers: AxiosHeaders.from(response.headers),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
config,
|
||||
request
|
||||
})
|
||||
})
|
||||
} catch (err) {
|
||||
unsubscribe && unsubscribe();
|
||||
|
||||
if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
|
||||
throw Object.assign(
|
||||
new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
|
||||
{
|
||||
cause: err.cause || err
|
||||
if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
|
||||
headers.setContentType(contentTypeHeader)
|
||||
}
|
||||
)
|
||||
|
||||
if (_request.body) {
|
||||
const [onProgress, flush] = progressEventDecorator(
|
||||
requestContentLength,
|
||||
progressEventReducer(asyncDecorator(onUploadProgress))
|
||||
);
|
||||
|
||||
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
|
||||
}
|
||||
}
|
||||
|
||||
if (!utils.isString(withCredentials)) {
|
||||
withCredentials = withCredentials ? 'include' : 'omit';
|
||||
}
|
||||
|
||||
// Cloudflare Workers throws when credentials are defined
|
||||
// see https://github.com/cloudflare/workerd/issues/902
|
||||
const isCredentialsSupported = isRequestSupported && "credentials" in Request.prototype;
|
||||
|
||||
const resolvedOptions = {
|
||||
...fetchOptions,
|
||||
signal: composedSignal,
|
||||
method: method.toUpperCase(),
|
||||
headers: headers.normalize().toJSON(),
|
||||
body: data,
|
||||
duplex: "half",
|
||||
credentials: isCredentialsSupported ? withCredentials : undefined
|
||||
};
|
||||
|
||||
request = isRequestSupported && new Request(url, resolvedOptions);
|
||||
|
||||
let response = await (isRequestSupported ? fetch(request, fetchOptions) : fetch(url, resolvedOptions));
|
||||
|
||||
const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
|
||||
|
||||
if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
|
||||
const options = {};
|
||||
|
||||
['status', 'statusText', 'headers'].forEach(prop => {
|
||||
options[prop] = response[prop];
|
||||
});
|
||||
|
||||
const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
|
||||
|
||||
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
|
||||
responseContentLength,
|
||||
progressEventReducer(asyncDecorator(onDownloadProgress), true)
|
||||
) || [];
|
||||
|
||||
response = new Response(
|
||||
trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
|
||||
flush && flush();
|
||||
unsubscribe && unsubscribe();
|
||||
}),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
responseType = responseType || 'text';
|
||||
|
||||
let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](response, config);
|
||||
|
||||
!isStreamResponse && unsubscribe && unsubscribe();
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
settle(resolve, reject, {
|
||||
data: responseData,
|
||||
headers: AxiosHeaders.from(response.headers),
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
config,
|
||||
request
|
||||
})
|
||||
})
|
||||
} catch (err) {
|
||||
unsubscribe && unsubscribe();
|
||||
|
||||
if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
|
||||
throw Object.assign(
|
||||
new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
|
||||
{
|
||||
cause: err.cause || err
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
throw AxiosError.from(err, err && err.code, config, request);
|
||||
}
|
||||
|
||||
throw AxiosError.from(err, err && err.code, config, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const seedCache = new Map();
|
||||
|
||||
export const getFetch = (config) => {
|
||||
let env = utils.merge.call({
|
||||
skipUndefined: true
|
||||
}, globalFetchAPI, config ? config.env : null);
|
||||
|
||||
const {fetch, Request, Response} = env;
|
||||
|
||||
const seeds = [
|
||||
Request, Response, fetch
|
||||
];
|
||||
|
||||
let len = seeds.length, i = len,
|
||||
seed, target, map = seedCache;
|
||||
|
||||
while (i--) {
|
||||
seed = seeds[i];
|
||||
target = map.get(seed);
|
||||
|
||||
target === undefined && map.set(seed, target = (i ? new Map() : factory(env)))
|
||||
|
||||
map = target;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
const adapter = getFetch();
|
||||
|
||||
export default adapter;
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function dispatchRequest(config) {
|
||||
config.headers.setContentType('application/x-www-form-urlencoded', false);
|
||||
}
|
||||
|
||||
const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
|
||||
const adapter = adapters.getAdapter(config.adapter || defaults.adapter, config);
|
||||
|
||||
return adapter(config).then(function onAdapterResolution(response) {
|
||||
throwIfCancellationRequested(config);
|
||||
|
||||
+5
-3
@@ -148,7 +148,7 @@ const isEmptyObject = (val) => {
|
||||
if (!isObject(val) || isBuffer(val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
return Object.keys(val).length === 0 && Object.getPrototypeOf(val) === Object.prototype;
|
||||
} catch (e) {
|
||||
@@ -341,7 +341,7 @@ const isContextDefined = (context) => !isUndefined(context) && context !== _glob
|
||||
* @returns {Object} Result of all merge properties
|
||||
*/
|
||||
function merge(/* obj1, obj2, obj3, ... */) {
|
||||
const {caseless} = isContextDefined(this) && this || {};
|
||||
const {caseless, skipUndefined} = isContextDefined(this) && this || {};
|
||||
const result = {};
|
||||
const assignValue = (val, key) => {
|
||||
const targetKey = caseless && findKey(result, key) || key;
|
||||
@@ -352,7 +352,9 @@ function merge(/* obj1, obj2, obj3, ... */) {
|
||||
} else if (isArray(val)) {
|
||||
result[targetKey] = val.slice();
|
||||
} else {
|
||||
result[targetKey] = val;
|
||||
if (!skipUndefined || !isUndefined(val)) {
|
||||
result[targetKey] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -399,14 +399,95 @@ describe('supports fetch with nodejs', function () {
|
||||
const FormData = (await import('form-data')).default; // Node FormData
|
||||
const form = new FormData();
|
||||
form.append('foo', 'bar');
|
||||
|
||||
|
||||
server = await startHTTPServer((req, res) => {
|
||||
const contentType = req.headers['content-type'];
|
||||
assert.match(contentType, /^multipart\/form-data; boundary=/i);
|
||||
res.end('OK');
|
||||
});
|
||||
|
||||
|
||||
await fetchAxios.post('/form', form);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('env config', () => {
|
||||
it('should respect env fetch API configuration', async() => {
|
||||
const { data, headers } = await fetchAxios.get('/', {
|
||||
env: {
|
||||
fetch() {
|
||||
return {
|
||||
headers: {
|
||||
foo: '1'
|
||||
},
|
||||
text: async () => 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(headers.get('foo'), '1');
|
||||
assert.strictEqual(data, 'test');
|
||||
});
|
||||
|
||||
it('should be able to request with lack of Request object', async() => {
|
||||
const form = new FormData();
|
||||
|
||||
form.append('x', '1');
|
||||
|
||||
const { data, headers } = await fetchAxios.post('/', form, {
|
||||
onUploadProgress() {
|
||||
// dummy listener to activate streaming
|
||||
},
|
||||
env: {
|
||||
Request: null,
|
||||
fetch() {
|
||||
return {
|
||||
headers: {
|
||||
foo: '1'
|
||||
},
|
||||
text: async () => 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(headers.get('foo'), '1');
|
||||
assert.strictEqual(data, 'test');
|
||||
});
|
||||
|
||||
it('should be able to handle response with lack of Response object', async() => {
|
||||
const { data, headers } = await fetchAxios.get('/', {
|
||||
onDownloadProgress() {
|
||||
// dummy listener to activate streaming
|
||||
},
|
||||
env: {
|
||||
Request: null,
|
||||
Response: null,
|
||||
fetch() {
|
||||
return {
|
||||
headers: {
|
||||
foo: '1'
|
||||
},
|
||||
text: async () => 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(headers.get('foo'), '1');
|
||||
assert.strictEqual(data, 'test');
|
||||
});
|
||||
|
||||
it('should fallback to the global on undefined env value', async() => {
|
||||
server = await startHTTPServer((req, res) => res.end('OK'));
|
||||
|
||||
const { data } = await fetchAxios.get('/', {
|
||||
env: {
|
||||
fetch: undefined
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(data, 'OK');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user