fix(ajax): bug fixes and readability improvements
This commit is contained in:
parent
5633d2aba8
commit
9b79b287e9
6 changed files with 116 additions and 100 deletions
5
.changeset/eight-pumpkins-clean.md
Normal file
5
.changeset/eight-pumpkins-clean.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/ajax': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix(ajax) options expansion, fix removing request interceptors, use 1 hour as default time to live, check for null when serializing the search params
|
||||||
|
|
@ -27,11 +27,11 @@ export class AjaxClient {
|
||||||
xsrfCookieName: 'XSRF-TOKEN',
|
xsrfCookieName: 'XSRF-TOKEN',
|
||||||
xsrfHeaderName: 'X-XSRF-TOKEN',
|
xsrfHeaderName: 'X-XSRF-TOKEN',
|
||||||
jsonPrefix: '',
|
jsonPrefix: '',
|
||||||
|
...config,
|
||||||
cacheOptions: {
|
cacheOptions: {
|
||||||
getCacheIdentifier: () => '_default',
|
getCacheIdentifier: () => '_default',
|
||||||
...config.cacheOptions,
|
...config.cacheOptions,
|
||||||
},
|
},
|
||||||
...config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {Array.<RequestInterceptor|CachedRequestInterceptor>} */
|
/** @type {Array.<RequestInterceptor|CachedRequestInterceptor>} */
|
||||||
|
|
@ -43,24 +43,18 @@ export class AjaxClient {
|
||||||
this.addRequestInterceptor(acceptLanguageRequestInterceptor);
|
this.addRequestInterceptor(acceptLanguageRequestInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.__config.xsrfCookieName && this.__config.xsrfHeaderName) {
|
const { xsrfCookieName, xsrfHeaderName } = this.__config;
|
||||||
this.addRequestInterceptor(
|
if (xsrfCookieName && xsrfHeaderName) {
|
||||||
createXSRFRequestInterceptor(this.__config.xsrfCookieName, this.__config.xsrfHeaderName),
|
this.addRequestInterceptor(createXSRFRequestInterceptor(xsrfCookieName, xsrfHeaderName));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.__config.cacheOptions && this.__config.cacheOptions.useCache) {
|
const { cacheOptions } = this.__config;
|
||||||
|
if (cacheOptions?.useCache) {
|
||||||
this.addRequestInterceptor(
|
this.addRequestInterceptor(
|
||||||
cacheRequestInterceptorFactory(
|
cacheRequestInterceptorFactory(cacheOptions.getCacheIdentifier, cacheOptions),
|
||||||
this.__config.cacheOptions.getCacheIdentifier,
|
|
||||||
this.__config.cacheOptions,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
this.addResponseInterceptor(
|
this.addResponseInterceptor(
|
||||||
cacheResponseInterceptorFactory(
|
cacheResponseInterceptorFactory(cacheOptions.getCacheIdentifier, cacheOptions),
|
||||||
this.__config.cacheOptions.getCacheIdentifier,
|
|
||||||
this.__config.cacheOptions,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,10 +78,9 @@ export class AjaxClient {
|
||||||
|
|
||||||
/** @param {RequestInterceptor} requestInterceptor */
|
/** @param {RequestInterceptor} requestInterceptor */
|
||||||
removeRequestInterceptor(requestInterceptor) {
|
removeRequestInterceptor(requestInterceptor) {
|
||||||
const indexOf = this._requestInterceptors.indexOf(requestInterceptor);
|
this._requestInterceptors = this._requestInterceptors.filter(
|
||||||
if (indexOf !== -1) {
|
interceptor => interceptor !== requestInterceptor,
|
||||||
this._requestInterceptors.splice(indexOf);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {ResponseInterceptor} responseInterceptor */
|
/** @param {ResponseInterceptor} responseInterceptor */
|
||||||
|
|
@ -97,10 +90,9 @@ export class AjaxClient {
|
||||||
|
|
||||||
/** @param {ResponseInterceptor} responseInterceptor */
|
/** @param {ResponseInterceptor} responseInterceptor */
|
||||||
removeResponseInterceptor(responseInterceptor) {
|
removeResponseInterceptor(responseInterceptor) {
|
||||||
const indexOf = this._responseInterceptors.indexOf(responseInterceptor);
|
this._responseInterceptors = this._responseInterceptors.filter(
|
||||||
if (indexOf !== -1) {
|
interceptor => interceptor !== responseInterceptor,
|
||||||
this._responseInterceptors.splice(indexOf, 1);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -117,9 +109,9 @@ export class AjaxClient {
|
||||||
request.params = init?.params;
|
request.params = init?.params;
|
||||||
|
|
||||||
// run request interceptors, returning directly and skipping the network
|
// run request interceptors, returning directly and skipping the network
|
||||||
// if a interceptor returns a Response
|
|
||||||
const interceptedRequestOrResponse = await this.__interceptRequest(request);
|
const interceptedRequestOrResponse = await this.__interceptRequest(request);
|
||||||
if (interceptedRequestOrResponse instanceof Response) {
|
if (interceptedRequestOrResponse instanceof Response) {
|
||||||
|
// prevent network request, return cached response
|
||||||
return interceptedRequestOrResponse;
|
return interceptedRequestOrResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,12 +139,12 @@ export class AjaxClient {
|
||||||
const lionInit = {
|
const lionInit = {
|
||||||
...init,
|
...init,
|
||||||
headers: {
|
headers: {
|
||||||
...(init && init.headers),
|
...init?.headers,
|
||||||
accept: 'application/json',
|
accept: 'application/json',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (lionInit && lionInit.body) {
|
if (lionInit?.body) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
lionInit.headers['content-type'] = 'application/json';
|
lionInit.headers['content-type'] = 'application/json';
|
||||||
lionInit.body = JSON.stringify(lionInit.body);
|
lionInit.body = JSON.stringify(lionInit.body);
|
||||||
|
|
@ -163,10 +155,9 @@ export class AjaxClient {
|
||||||
const response = await this.request(info, jsonInit);
|
const response = await this.request(info, jsonInit);
|
||||||
let responseText = await response.text();
|
let responseText = await response.text();
|
||||||
|
|
||||||
if (typeof this.__config.jsonPrefix === 'string') {
|
const { jsonPrefix } = this.__config;
|
||||||
if (responseText.startsWith(this.__config.jsonPrefix)) {
|
if (typeof jsonPrefix === 'string' && responseText.startsWith(jsonPrefix)) {
|
||||||
responseText = responseText.substring(this.__config.jsonPrefix.length);
|
responseText = responseText.substring(jsonPrefix.length);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,11 @@ import './typedef.js';
|
||||||
const SECOND = 1000;
|
const SECOND = 1000;
|
||||||
const MINUTE = SECOND * 60;
|
const MINUTE = SECOND * 60;
|
||||||
const HOUR = MINUTE * 60;
|
const HOUR = MINUTE * 60;
|
||||||
|
const DEFAULT_TIME_TO_LIVE = HOUR;
|
||||||
|
|
||||||
class Cache {
|
class Cache {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.expiration = new Date().getTime() + HOUR;
|
this.expiration = new Date().getTime() + DEFAULT_TIME_TO_LIVE;
|
||||||
/**
|
|
||||||
* @type {{[url: string]: CacheConfig }}
|
|
||||||
*/
|
|
||||||
this.cacheConfig = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{[url: string]: {expires: number, response: CacheResponse} }}
|
* @type {{[url: string]: {expires: number, response: CacheResponse} }}
|
||||||
* @private
|
* @private
|
||||||
|
|
@ -97,7 +93,7 @@ class Cache {
|
||||||
this._validateCache();
|
this._validateCache();
|
||||||
|
|
||||||
Object.keys(this._cacheObject).forEach(key => {
|
Object.keys(this._cacheObject).forEach(key => {
|
||||||
if (key.indexOf(url) > -1) {
|
if (key.includes(url)) {
|
||||||
delete this._cacheObject[key];
|
delete this._cacheObject[key];
|
||||||
this.resolvePendingRequest(key);
|
this.resolvePendingRequest(key);
|
||||||
}
|
}
|
||||||
|
|
@ -113,15 +109,9 @@ class Cache {
|
||||||
|
|
||||||
Object.keys(this._cacheObject).forEach(key => {
|
Object.keys(this._cacheObject).forEach(key => {
|
||||||
const notMatch = !new RegExp(regex).test(key);
|
const notMatch = !new RegExp(regex).test(key);
|
||||||
|
|
||||||
if (notMatch) return;
|
if (notMatch) return;
|
||||||
|
delete this._cacheObject[key];
|
||||||
const isDataDeleted = delete this._cacheObject[key];
|
|
||||||
this.resolvePendingRequest(key);
|
this.resolvePendingRequest(key);
|
||||||
|
|
||||||
if (!isDataDeleted) {
|
|
||||||
throw new Error(`Failed to delete cache for a request '${key}'`);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,6 +125,7 @@ class Cache {
|
||||||
if (new Date().getTime() > this.expiration) {
|
if (new Date().getTime() > this.expiration) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._cacheObject = {};
|
this._cacheObject = {};
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +141,7 @@ let caches = {};
|
||||||
*/
|
*/
|
||||||
export const searchParamSerializer = (params = {}) =>
|
export const searchParamSerializer = (params = {}) =>
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
typeof params === 'object' ? new URLSearchParams(params).toString() : '';
|
typeof params === 'object' && params !== null ? new URLSearchParams(params).toString() : '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the active cache instance for the current session
|
* Returns the active cache instance for the current session
|
||||||
|
|
@ -159,10 +150,9 @@ export const searchParamSerializer = (params = {}) =>
|
||||||
* @param {string} cacheIdentifier usually the refreshToken of the owner of the cache
|
* @param {string} cacheIdentifier usually the refreshToken of the owner of the cache
|
||||||
*/
|
*/
|
||||||
const getCache = cacheIdentifier => {
|
const getCache = cacheIdentifier => {
|
||||||
if (caches[cacheIdentifier] && caches[cacheIdentifier]._validateCache()) {
|
if (caches[cacheIdentifier]?._validateCache()) {
|
||||||
return caches[cacheIdentifier];
|
return caches[cacheIdentifier];
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalidate old caches
|
// invalidate old caches
|
||||||
caches = {};
|
caches = {};
|
||||||
// create new cache
|
// create new cache
|
||||||
|
|
@ -193,7 +183,7 @@ export const validateOptions = ({
|
||||||
|
|
||||||
// validate 'timeToLive', default 1 hour
|
// validate 'timeToLive', default 1 hour
|
||||||
if (timeToLive === undefined) {
|
if (timeToLive === undefined) {
|
||||||
timeToLive = 0;
|
timeToLive = DEFAULT_TIME_TO_LIVE;
|
||||||
}
|
}
|
||||||
if (Number.isNaN(parseInt(String(timeToLive), 10))) {
|
if (Number.isNaN(parseInt(String(timeToLive), 10))) {
|
||||||
throw new Error('Property `timeToLive` must be of type `number`');
|
throw new Error('Property `timeToLive` must be of type `number`');
|
||||||
|
|
@ -213,7 +203,7 @@ export const validateOptions = ({
|
||||||
// validate 'requestIdentificationFn', default is url + searchParams
|
// validate 'requestIdentificationFn', default is url + searchParams
|
||||||
if (requestIdentificationFn) {
|
if (requestIdentificationFn) {
|
||||||
if (typeof requestIdentificationFn !== 'function') {
|
if (typeof requestIdentificationFn !== 'function') {
|
||||||
throw new Error('Property `requestIdentificationFn` must be of type `function` or `falsy`');
|
throw new Error('Property `requestIdentificationFn` must be of type `function`');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
requestIdentificationFn = /** @param {any} data */ (
|
requestIdentificationFn = /** @param {any} data */ (
|
||||||
|
|
@ -235,25 +225,6 @@ export const validateOptions = ({
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Request interceptor to return relevant cached requests
|
|
||||||
* @param {ValidatedCacheOptions} validatedInitialCacheOptions
|
|
||||||
* @param {CacheOptions=} configCacheOptions
|
|
||||||
* @returns {ValidatedCacheOptions}
|
|
||||||
*/
|
|
||||||
function composeCacheOptions(validatedInitialCacheOptions, configCacheOptions) {
|
|
||||||
let actionCacheOptions = validatedInitialCacheOptions;
|
|
||||||
|
|
||||||
if (configCacheOptions) {
|
|
||||||
actionCacheOptions = validateOptions({
|
|
||||||
...validatedInitialCacheOptions,
|
|
||||||
...configCacheOptions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return actionCacheOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request interceptor to return relevant cached requests
|
* Request interceptor to return relevant cached requests
|
||||||
* @param {function(): string} getCacheIdentifier used to invalidate cache if identifier is changed
|
* @param {function(): string} getCacheIdentifier used to invalidate cache if identifier is changed
|
||||||
|
|
@ -264,12 +235,11 @@ export const cacheRequestInterceptorFactory = (getCacheIdentifier, globalCacheOp
|
||||||
const validatedInitialCacheOptions = validateOptions(globalCacheOptions);
|
const validatedInitialCacheOptions = validateOptions(globalCacheOptions);
|
||||||
|
|
||||||
return /** @param {CacheRequest} cacheRequest */ async cacheRequest => {
|
return /** @param {CacheRequest} cacheRequest */ async cacheRequest => {
|
||||||
const { method } = cacheRequest;
|
const cacheOptions = validateOptions({
|
||||||
|
...validatedInitialCacheOptions,
|
||||||
|
...cacheRequest.cacheOptions,
|
||||||
|
});
|
||||||
|
|
||||||
const cacheOptions = composeCacheOptions(
|
|
||||||
validatedInitialCacheOptions,
|
|
||||||
cacheRequest.cacheOptions,
|
|
||||||
);
|
|
||||||
cacheRequest.cacheOptions = cacheOptions;
|
cacheRequest.cacheOptions = cacheOptions;
|
||||||
|
|
||||||
// don't use cache if 'useCache' === false
|
// don't use cache if 'useCache' === false
|
||||||
|
|
@ -278,12 +248,12 @@ export const cacheRequestInterceptorFactory = (getCacheIdentifier, globalCacheOp
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheId = cacheOptions.requestIdentificationFn(cacheRequest, searchParamSerializer);
|
const cacheId = cacheOptions.requestIdentificationFn(cacheRequest, searchParamSerializer);
|
||||||
|
|
||||||
// cacheIdentifier is used to bind the cache to the current session
|
// cacheIdentifier is used to bind the cache to the current session
|
||||||
const currentCache = getCache(getCacheIdentifier());
|
const currentCache = getCache(getCacheIdentifier());
|
||||||
|
const { method } = cacheRequest;
|
||||||
|
|
||||||
// don't use cache if the request method is not part of the configs methods
|
// don't use cache if the request method is not part of the configs methods
|
||||||
if (cacheOptions.methods.indexOf(method.toLowerCase()) === -1) {
|
if (!cacheOptions.methods.includes(method.toLowerCase())) {
|
||||||
// If it's NOT one of the config.methods, invalidate caches
|
// If it's NOT one of the config.methods, invalidate caches
|
||||||
currentCache.delete(cacheId);
|
currentCache.delete(cacheId);
|
||||||
// also invalidate caches matching to cacheOptions
|
// also invalidate caches matching to cacheOptions
|
||||||
|
|
@ -310,11 +280,7 @@ export const cacheRequestInterceptorFactory = (getCacheIdentifier, globalCacheOp
|
||||||
|
|
||||||
const cacheResponse = currentCache.get(cacheId, cacheOptions.timeToLive);
|
const cacheResponse = currentCache.get(cacheId, cacheOptions.timeToLive);
|
||||||
if (cacheResponse) {
|
if (cacheResponse) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
cacheRequest.cacheOptions = cacheRequest.cacheOptions ?? { useCache: false };
|
||||||
if (!cacheRequest.cacheOptions) {
|
|
||||||
cacheRequest.cacheOptions = { useCache: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = /** @type {CacheResponse} */ cacheResponse.clone();
|
const response = /** @type {CacheResponse} */ cacheResponse.clone();
|
||||||
response.request = cacheRequest;
|
response.request = cacheRequest;
|
||||||
response.fromCache = true;
|
response.fromCache = true;
|
||||||
|
|
@ -350,10 +316,11 @@ export const cacheResponseInterceptorFactory = (getCacheIdentifier, globalCacheO
|
||||||
throw new Error('Missing request in response.');
|
throw new Error('Missing request in response.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheOptions = composeCacheOptions(
|
const cacheOptions = validateOptions({
|
||||||
validatedInitialCacheOptions,
|
...validatedInitialCacheOptions,
|
||||||
cacheResponse.request?.cacheOptions,
|
...cacheResponse.request?.cacheOptions,
|
||||||
);
|
});
|
||||||
|
|
||||||
// string that identifies cache entry
|
// string that identifies cache entry
|
||||||
const cacheId = cacheOptions.requestIdentificationFn(
|
const cacheId = cacheOptions.requestIdentificationFn(
|
||||||
cacheResponse.request,
|
cacheResponse.request,
|
||||||
|
|
@ -363,13 +330,11 @@ export const cacheResponseInterceptorFactory = (getCacheIdentifier, globalCacheO
|
||||||
const isAlreadyFromCache = !!cacheResponse.fromCache;
|
const isAlreadyFromCache = !!cacheResponse.fromCache;
|
||||||
// caching all responses with not default `timeToLive`
|
// caching all responses with not default `timeToLive`
|
||||||
const isCacheActive = cacheOptions.timeToLive > 0;
|
const isCacheActive = cacheOptions.timeToLive > 0;
|
||||||
|
const isMethodSupported = cacheOptions.methods.includes(
|
||||||
|
cacheResponse.request.method.toLowerCase(),
|
||||||
|
);
|
||||||
// if the request is one of the options.methods; store response in cache
|
// if the request is one of the options.methods; store response in cache
|
||||||
if (
|
if (!isAlreadyFromCache && isCacheActive && isMethodSupported) {
|
||||||
!isAlreadyFromCache &&
|
|
||||||
isCacheActive &&
|
|
||||||
cacheOptions.methods.indexOf(cacheResponse.request.method.toLowerCase()) > -1
|
|
||||||
) {
|
|
||||||
// store the response data in the cache and mark request as resolved
|
// store the response data in the cache and mark request as resolved
|
||||||
currentCache.set(cacheId, cacheResponse.clone());
|
currentCache.set(cacheId, cacheResponse.clone());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ export function getCookie(name, _document = document) {
|
||||||
*/
|
*/
|
||||||
export async function acceptLanguageRequestInterceptor(request) {
|
export async function acceptLanguageRequestInterceptor(request) {
|
||||||
if (!request.headers.has('accept-language')) {
|
if (!request.headers.has('accept-language')) {
|
||||||
let locale = document.documentElement.lang || 'en';
|
const documentLocale = document.documentElement.lang;
|
||||||
if (document.documentElement.getAttribute('data-localize-lang')) {
|
const localizeLang = document.documentElement.getAttribute('data-localize-lang');
|
||||||
locale = document.documentElement.getAttribute('data-localize-lang') || 'en';
|
const locale = localizeLang || documentLocale || 'en';
|
||||||
}
|
|
||||||
request.headers.set('accept-language', locale);
|
request.headers.set('accept-language', locale);
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,54 @@ describe('AjaxClient', () => {
|
||||||
fetchStub.restore();
|
fetchStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('options', () => {
|
||||||
|
it('creates options object by expanding cacheOptions', async () => {
|
||||||
|
// Given
|
||||||
|
const getCacheIdentifier = () => '_DEFAULT_CACHE_ID';
|
||||||
|
const config = {
|
||||||
|
jsonPrefix: ")]}',",
|
||||||
|
cacheOptions: {
|
||||||
|
useCache: true,
|
||||||
|
timeToLive: 1000 * 60 * 5, // 5 minutes
|
||||||
|
getCacheIdentifier,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const expected = {
|
||||||
|
addAcceptLanguage: true,
|
||||||
|
xsrfCookieName: 'XSRF-TOKEN',
|
||||||
|
xsrfHeaderName: 'X-XSRF-TOKEN',
|
||||||
|
jsonPrefix: ")]}',",
|
||||||
|
cacheOptions: {
|
||||||
|
useCache: true,
|
||||||
|
timeToLive: 300000,
|
||||||
|
getCacheIdentifier,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// When
|
||||||
|
const ajax1 = new AjaxClient(config);
|
||||||
|
const result = ajax1.options;
|
||||||
|
// Then
|
||||||
|
expect(result).to.deep.equal(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has default getCacheIdentifier function when cacheOptions does not provide one', async () => {
|
||||||
|
// Given
|
||||||
|
const config = {
|
||||||
|
cacheOptions: {
|
||||||
|
useCache: true,
|
||||||
|
timeToLive: 1000 * 60 * 5, // 5 minutes
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// When
|
||||||
|
// @ts-expect-error
|
||||||
|
const ajax1 = new AjaxClient(config);
|
||||||
|
const result = ajax1.options?.cacheOptions?.getCacheIdentifier;
|
||||||
|
// Then
|
||||||
|
expect(result).not.to.be.undefined;
|
||||||
|
expect(result).to.be.a('function');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('request()', () => {
|
describe('request()', () => {
|
||||||
it('calls fetch with the given args, returning the result', async () => {
|
it('calls fetch with the given args, returning the result', async () => {
|
||||||
const response = await (await ajax.request('/foo', { method: 'POST' })).text();
|
const response = await (await ajax.request('/foo', { method: 'POST' })).text();
|
||||||
|
|
@ -130,15 +178,22 @@ describe('AjaxClient', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removeRequestInterceptor() removes a request interceptor', async () => {
|
it('removeRequestInterceptor() removes a request interceptor', async () => {
|
||||||
const interceptor = /** @param {Request} r */ async r =>
|
const interceptor1 = /** @param {Request} r */ async r =>
|
||||||
new Request(`${r.url}/intercepted-1`);
|
new Request(`${r.url}/intercepted-1`);
|
||||||
ajax.addRequestInterceptor(interceptor);
|
const interceptor2 = /** @param {Request} r */ async r =>
|
||||||
ajax.removeRequestInterceptor(interceptor);
|
new Request(`${r.url}/intercepted-2`);
|
||||||
|
const interceptor3 = /** @param {Request} r */ async r =>
|
||||||
|
new Request(`${r.url}/intercepted-3`);
|
||||||
|
|
||||||
|
ajax.addRequestInterceptor(interceptor1);
|
||||||
|
ajax.addRequestInterceptor(interceptor2);
|
||||||
|
ajax.addRequestInterceptor(interceptor3);
|
||||||
|
ajax.removeRequestInterceptor(interceptor1);
|
||||||
|
|
||||||
await ajax.request('/foo', { method: 'POST' });
|
await ajax.request('/foo', { method: 'POST' });
|
||||||
|
|
||||||
const request = fetchStub.getCall(0).args[0];
|
const request = fetchStub.getCall(0).args[0];
|
||||||
expect(request.url).to.equal(`${window.location.origin}/foo`);
|
expect(request.url).to.equal(`${window.location.origin}/foo/intercepted-2/intercepted-3`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removeResponseInterceptor() removes a request interceptor', async () => {
|
it('removeResponseInterceptor() removes a request interceptor', async () => {
|
||||||
|
|
@ -148,8 +203,9 @@ describe('AjaxClient', () => {
|
||||||
// @ts-expect-error we're mocking the response as a simple promise which returns a string
|
// @ts-expect-error we're mocking the response as a simple promise which returns a string
|
||||||
ajax.removeResponseInterceptor(interceptor);
|
ajax.removeResponseInterceptor(interceptor);
|
||||||
|
|
||||||
const response = await (await ajax.request('/foo', { method: 'POST' })).text();
|
const response = await ajax.request('/foo', { method: 'POST' });
|
||||||
expect(response).to.equal('mock response');
|
const text = await response.text();
|
||||||
|
expect(text).to.equal('mock response');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ describe('ajax cache', () => {
|
||||||
requestIdentificationFn: 'not a function',
|
requestIdentificationFn: 'not a function',
|
||||||
});
|
});
|
||||||
removeCacheInterceptors(ajax, indexes);
|
removeCacheInterceptors(ajax, indexes);
|
||||||
}).to.throw(/Property `requestIdentificationFn` must be of type `function` or `falsy`/);
|
}).to.throw(/Property `requestIdentificationFn` must be of type `function`/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue