Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@lion/ajax

Package Overview
Dependencies
Maintainers
1
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lion/ajax - npm Package Compare versions

Comparing version 0.5.15 to 0.6.0

custom-elements.json

11

CHANGELOG.md
# Change Log
## 0.6.0
### Minor Changes
- 4452d06d: BREAKING CHANGE: We no longer use axios! Our ajax package is now a thin wrapper around Fetch. The API has changed completely. You will need a fetch polyfill for IE11.
- b2f981db: Add exports field in package.json
Note that some tools can break with this change as long as they respect the exports field. If that is the case, check that you always access the elements included in the exports field, with the same name which they are exported. Any item not exported is considered private to the package and should not be accessed from the outside.
- bbffd710: Added Ajax cache interceptors.
## 0.5.15

@@ -4,0 +15,0 @@

7

index.d.ts

@@ -1,4 +0,5 @@

export { AjaxClass } from "./src/AjaxClass.js";
export { jsonPrefixTransformerFactory } from "./src/transformers.js";
export { AjaxClient } from "./src/AjaxClient.js";
export { AjaxClientFetchError } from "./src/AjaxClientFetchError.js";
export { ajax, setAjax } from "./src/ajax.js";
export { cancelInterceptorFactory, cancelPreviousOnNewRequestInterceptorFactory, addAcceptLanguageHeaderInterceptorFactory } from "./src/interceptors.js";
export { acceptLanguageRequestInterceptor, createXSRFRequestInterceptor, getCookie } from "./src/interceptors.js";
export { cacheRequestInterceptorFactory, cacheResponseInterceptorFactory, validateOptions } from "./src/interceptors-cache.js";
export { ajax, setAjax } from './src/ajax.js';
export { AjaxClient } from './src/AjaxClient.js';
export { AjaxClientFetchError } from './src/AjaxClientFetchError.js';
export { AjaxClass } from './src/AjaxClass.js';
export {
cancelInterceptorFactory,
cancelPreviousOnNewRequestInterceptorFactory,
addAcceptLanguageHeaderInterceptorFactory,
acceptLanguageRequestInterceptor,
createXSRFRequestInterceptor,
getCookie,
} from './src/interceptors.js';
export { jsonPrefixTransformerFactory } from './src/transformers.js';
export {
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
validateOptions,
} from './src/interceptors-cache.js';
{
"name": "@lion/ajax",
"version": "0.5.15",
"description": "Thin wrapper around axios to allow for custom interceptors",
"version": "0.6.0",
"description": "Thin wrapper around fetch with support for interceptors.",
"license": "MIT",

@@ -18,2 +18,3 @@ "author": "ing-bank",

"*.js",
"custom-elements.json",
"docs",

@@ -27,16 +28,13 @@ "src",

"scripts": {
"custom-elements-manifest": "custom-elements-manifest analyze --exclude 'docs/**/*'",
"debug": "cd ../../ && npm run debug -- --group ajax",
"debug:firefox": "cd ../../ && npm run debug:firefox -- --group ajax",
"debug:webkit": "cd ../../ && npm run debug:webkit -- --group ajax",
"prepublishOnly": "../../scripts/npm-prepublish.js",
"prepublishOnly": "../../scripts/npm-prepublish.js && npm run custom-elements-manifest",
"test": "cd ../../ && npm run test:browser -- --group ajax"
},
"sideEffects": false,
"dependencies": {
"@bundled-es-modules/axios": "0.18.1",
"@lion/core": "0.13.8",
"singleton-manager": "1.2.1"
},
"keywords": [
"ajax",
"fetch",
"http",
"lion",

@@ -47,3 +45,5 @@ "web-components"

"access": "public"
}
}
},
"exports": "./index.js",
"customElementsManifest": "custom-elements.json"
}

@@ -0,188 +1,237 @@

[//]: # 'AUTO INSERT HEADER PREPUBLISH'
# Ajax
`ajax` is the global manager for handling all ajax requests.
It is a promise based system for fetching data, based on [axios](https://github.com/axios/axios)
`ajax` is a small wrapper around `fetch` which:
```js script
import { html } from '@lion/core';
import { ajax } from './src/ajax.js';
import { AjaxClass } from './src/AjaxClass.js';
- Allows globally registering request and response interceptors
- Throws on 4xx and 5xx status codes
- Prevents network request if a request interceptor returns a response
- Supports a JSON request which automatically encodes/decodes body request and response payload as JSON
- Adds accept-language header to requests based on application language
- Adds XSRF header to request if the cookie is present
export default {
title: 'Others/Ajax',
};
## How to use
### Installation
```sh
npm i --save @lion/ajax
```
## Features
### Relation to fetch
- only JS functions, no (unnecessarily expensive) web components
- supports GET, POST, PUT, DELETE, REQUEST, PATCH and HEAD methods
- can be used with or without XSRF token
`ajax` delegates all requests to fetch. `ajax.request` and `ajax.requestJson` have the same function signature as `window.fetch`, you can use any online resource to learn more about fetch. [MDN](http://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) is a great start.
## How to use
### Example requests
### Installation
#### GET request
```bash
npm i --save @lion/ajax
```js
import { ajax } from '@lion/ajax';
const response = await ajax.request('/api/users');
const users = await response.json();
```
#### POST request
```js
import { ajax, AjaxClass } from '@lion/ajax';
import { ajax } from '@lion/ajax';
const response = await ajax.request('/api/users', {
method: 'POST',
body: JSON.stringify({ username: 'steve' }),
});
const newUser = await response.json();
```
### Example
### JSON requests
We usually deal with JSON requests and responses. With `requestJson` you don't need to specifically stringify the request body or parse the response body:
#### GET JSON request
```js
import { ajax } from '@lion/ajax';
ajax.get('data.json').then(response => console.log(response));
const { response, body } = await ajax.requestJson('/api/users');
```
### Performing requests
#### POST JSON request
Performing a `GET` request:
```js
import { ajax } from '@lion/ajax';
```js preview-story
export const performingGetRequests = () => html`
<button
@click=${() => {
ajax
.get('./packages/ajax/docs/assets/data.json')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
}}
>
Execute Request to Action Logger
</button>
`;
const { response, body } = await ajax.requestJson('/api/users', {
method: 'POST',
body: { username: 'steve' },
});
```
To post data to the server, pass the data as the second argument in the `POST` request:
### Error handling
Different from fetch, `ajax` throws when the server returns a 4xx or 5xx, returning the request and response:
```js
const body = {
ant: {
type: 'insect',
limbs: 6,
},
};
ajax
.post('zooApi/animals/addAnimal', body)
.then(response => {
console.log(`POST successful: ${response.status} ${response.statusText}`);
})
.catch(error => {
console.log(error);
});
import { ajax } from '@lion/ajax';
try {
const users = await ajax.requestJson('/api/users');
} catch (error) {
if (error.response) {
if (error.response.status === 400) {
// handle a specific status code, for example 400 bad request
} else {
console.error(error);
}
} else {
// an error happened before receiving a response, ex. an incorrect request or network error
console.error(error);
}
}
```
## Configuration
## Ajax Cache
### JSON prefix
A caching library that uses `lion-web/ajax` and adds cache interceptors to provide caching for use in
frontend `services`.
The called API might add a JSON prefix to the response in order to prevent hijacking.
The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
This prefix should be stripped before parsing the string as JSON.
Pass the prefix with the `jsonPrefix` option.
> Technical documentation and decisions can be found in
> [./docs/technical-docs.md](https://github.com/ing-bank/lion/blob/master/packages/ajax/docs/technical-docs.md)
### Getting started
Consume the global `ajax` instance and add the interceptors to it, using a cache configuration
which is applied on application level. If a developer wants to add specifics to cache behavior
they have to provide a cache config per action (`get`, `post`, etc.) via `cacheOptions` field of local ajax config,
see examples below.
> **Note**: make sure to add the **interceptors** only **once**. This is usually
> done on app-level
```js
const myAjax = new AjaxClass({ jsonPrefix: ")]}'," });
myAjax
.get('./packages/ajax/docs/assets/data.json')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
```
import {
ajax,
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
} from '@lion-web/ajax.js';
### Additional headers
const globalCacheOptions = {
useCache: true,
timeToLive: 1000 * 60 * 5, // 5 minutes
};
// Cache is removed each time an identifier changes,
// for instance when a current user is logged out
const getCacheIdentifier = () => getActiveProfile().profileId;
Add additional headers to the requests with the `headers` option.
ajax.addRequestInterceptor(cacheRequestInterceptorFactory(getCacheIdentifier, globalCacheOptions));
ajax.addResponseInterceptor(
cacheResponseInterceptorFactory(getCacheIdentifier, globalCacheOptions),
);
```js preview-story
export const additionalHeaders = () => html`
<button
@click=${() => {
const myAjax = new AjaxClass({ headers: { 'MY-HEADER': 'SOME-HEADER-VALUE' } });
myAjax
.get('./packages/ajax/docs/assets/data.json')
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
}}
>
Execute Request to Action Logger
</button>
`;
const { response, body } = await ajax.requestJson('/my-url');
```
When executing the request above, check the Network tab in the Browser's dev tools and look for the Request Header on the GET call.
### Ajax cache example
### Cancelable Request
```js
import {
ajax,
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
} from '@lion-web/ajax';
It is possible to make an Ajax request cancelable, and then call `cancel()` to make the request provide a custom error once fired.
const getCacheIdentifier = () => getActiveProfile().profileId;
```js preview-story
export const cancelableRequests = () => html`
<button
@click=${() => {
const myAjax = new AjaxClass({ cancelable: true });
requestAnimationFrame(() => {
myAjax.cancel('too slow');
});
myAjax
.get('./packages/ajax/docs/assets/data.json')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
}}
>
Execute Request to Action Logger
</button>
`;
const globalCacheOptions = {
useCache: false,
timeToLive: 50, // default: one hour (the cache instance will be replaced in 1 hour, regardless of this setting)
methods: ['get'], // default: ['get'] NOTE for now only 'get' is supported
// requestIdentificationFn: (requestConfig) => { }, // see docs below for more info
// invalidateUrls: [], see docs below for more info
// invalidateUrlsRegex: RegExp, // see docs below for more info
};
// pass a function to the interceptorFactory that retrieves a cache identifier
// ajax.interceptors.request.use(cacheRequestInterceptorFactory(getCacheIdentifier, cacheOptions));
// ajax.interceptors.response.use(
// cacheResponseInterceptorFactory(getCacheIdentifier, cacheOptions),
// );
class TodoService {
constructor() {
this.localAjaxConfig = {
cacheOptions: {
invalidateUrls: ['/api/todosbykeyword'], // default: []
},
};
}
/**
* Returns all todos from cache if not older than 5 minutes
*/
getTodos() {
return ajax.requestJson(`/api/todos`, this.localAjaxConfig);
}
/**
*
*/
getTodosByKeyword(keyword) {
return ajax.requestJson(`/api/todosbykeyword/${keyword}`, this.localAjaxConfig);
}
/**
* Creates new todo and invalidates cache.
* `getTodos` will NOT take the response from cache
*/
saveTodo(todo) {
return ajax.requestJson(`/api/todos`, { method: 'POST', body: todo, ...this.localAjaxConfig });
}
}
```
### Cancel concurrent requests
If a value returned by `cacheIdentifier` changes the cache is reset. We avoid situation of accessing old cache and proactively clean it, for instance when a user session is ended.
You can cancel concurrent requests with the `cancelPreviousOnNewRequest` option.
### Ajax cache Options
```js preview-story
export const cancelConcurrentRequests = () => html`
<button
@click=${() => {
const myAjax = new AjaxClass({ cancelPreviousOnNewRequest: true });
myAjax
.get('./packages/ajax/docs/assets/data.json')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error.message);
});
myAjax
.get('./packages/ajax/docs/assets/data.json')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error.message);
});
}}
>
Execute Both Requests to Action Logger
</button>
`;
```js
const cacheOptions = {
// `useCache`: determines wether or not to use the cache
// can be boolean
// default: false
useCache: true,
// `timeToLive`: is the time the cache should be kept in ms
// default: 0
// Note: regardless of this setting, the cache instance holding all the caches
// will be invalidated after one hour
timeToLive: 1000 * 60 * 5,
// `methods`: an array of methods on which this configuration is applied
// Note: when `useCache` is `false` this will not be used
// NOTE: ONLY GET IS SUPPORTED
// default: ['get']
methods: ['get'],
// `invalidateUrls`: an array of strings that for each string that partially
// occurs as key in the cache, will be removed
// default: []
// Note: can be invalidated only by non-get request to the same url
invalidateUrls: ['/api/todosbykeyword'],
// `invalidateUrlsRegex`: a RegExp object to match and delete
// each matched key in the cache
// Note: can be invalidated only by non-get request to the same url
invalidateUrlsRegex: /posts/
// `requestIdentificationFn`: a function to provide a string that should be
// taken as a key in the cache.
// This can be used to cache post-requests.
// default: (requestConfig, searchParamsSerializer) => url + params
requestIdentificationFn: (request, serializer) => {
return `${request.url}?${serializer(request.params)}`;
},
};
```

@@ -192,8 +241,6 @@

Due to a [bug in axios](https://github.com/axios/axios/issues/385) options may leak in to other instances.
So please avoid setting global options in axios. Interceptors have no issues.
## Fetch Polyfill
## Future plans
For IE11 you will need a polyfill for fetch. You should add this on your top level layer, e.g. your application.
- Eventually we want to remove axios and replace it with [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
- This wrapper exist to prevent this switch from causing breaking changes for our users
[This is the polyfill we recommend](https://github.com/github/fetch). It also has a [section for polyfilling AbortController](https://github.com/github/fetch#aborting-requests)

@@ -6,9 +6,6 @@ /**

* method is not called by any of your (indirect) dependencies.)
* @param {AjaxClass} newAjax the globally used instance of {@link:ajax}.
* @param {AjaxClient} newAjax the globally used instance of {@link:ajax}.
*/
export function setAjax(newAjax: AjaxClass): void;
/**
*
*/
export let ajax: any;
import { AjaxClass } from "./AjaxClass.js";
export function setAjax(newAjax: AjaxClient): void;
export let ajax: AjaxClient;
import { AjaxClient } from "./AjaxClient.js";

@@ -1,8 +0,4 @@

import { singletonManager } from 'singleton-manager';
import { AjaxClass } from './AjaxClass.js';
import { AjaxClient } from './AjaxClient.js';
/**
*
*/
export let ajax = singletonManager.get('@lion/ajax::ajax::0.3.x') || new AjaxClass(); // eslint-disable-line import/no-mutable-exports
export let ajax = new AjaxClient(); // eslint-disable-line import/no-mutable-exports

@@ -14,3 +10,3 @@ /**

* method is not called by any of your (indirect) dependencies.)
* @param {AjaxClass} newAjax the globally used instance of {@link:ajax}.
* @param {AjaxClient} newAjax the globally used instance of {@link:ajax}.
*/

@@ -17,0 +13,0 @@ export function setAjax(newAjax) {

/**
* @param {string} [lang]
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
* @param {string} name the cookie name
* @param {Document | { cookie: string }} _document overwriteable for testing
* @returns {string | null}
*/
export function addAcceptLanguageHeaderInterceptorFactory(lang?: string | undefined): (config: {
[key: string]: any;
}) => {
[key: string]: any;
};
export function getCookie(name: string, _document?: Document | {
cookie: string;
}): string | null;
/**
* @param {import('./AjaxClass').AjaxClass} ajaxInstance
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
* Transforms a request, adding an accept-language header with the current application's locale
* if it has not already been set.
* @type {RequestInterceptor}
*/
export function cancelInterceptorFactory(ajaxInstance: import('./AjaxClass').AjaxClass): (config: {
[key: string]: any;
}) => {
[key: string]: any;
};
export function acceptLanguageRequestInterceptor(request: Request): Promise<Request | Response>;
/**
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
* Creates a request transformer that adds a XSRF header for protecting
* against cross-site request forgery.
* @param {string} cookieName the cookie name
* @param {string} headerName the header name
* @param {Document | { cookie: string }} _document overwriteable for testing
* @returns {RequestInterceptor}
*/
export function cancelPreviousOnNewRequestInterceptorFactory(): (config: {
[key: string]: any;
}) => {
[key: string]: any;
};
export function createXSRFRequestInterceptor(cookieName: string, headerName: string, _document?: Document | {
cookie: string;
}): RequestInterceptor;

@@ -1,57 +0,50 @@

// @ts-ignore no types for bundled-es-modules/axios
import { axios } from '@bundled-es-modules/axios';
import './typedef.js';
/**
* @param {string} [lang]
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
* @param {string} name the cookie name
* @param {Document | { cookie: string }} _document overwriteable for testing
* @returns {string | null}
*/
export function addAcceptLanguageHeaderInterceptorFactory(lang) {
return /** @param {{[key:string]: ?}} config */ config => {
const result = config;
if (typeof lang === 'string' && lang !== '') {
if (typeof result.headers !== 'object') {
result.headers = {};
}
const withLang = { headers: { 'Accept-Language': lang, ...result.headers } };
return { ...result, ...withLang };
}
return result;
};
export function getCookie(name, _document = document) {
const match = _document.cookie.match(new RegExp(`(^|;\\s*)(${name})=([^;]*)`));
return match ? decodeURIComponent(match[3]) : null;
}
/**
* @param {import('./AjaxClass').AjaxClass} ajaxInstance
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
* Transforms a request, adding an accept-language header with the current application's locale
* if it has not already been set.
* @type {RequestInterceptor}
*/
export function cancelInterceptorFactory(ajaxInstance) {
/** @type {unknown[]} */
const cancelSources = [];
return /** @param {{[key:string]: ?}} config */ config => {
const source = axios.CancelToken.source();
cancelSources.push(source);
/* eslint-disable-next-line no-param-reassign */
ajaxInstance.cancel = (message = 'Operation canceled by the user.') => {
// @ts-ignore axios is untyped so we don't know the type for the source
cancelSources.forEach(s => s.cancel(message));
};
return { ...config, cancelToken: source.token };
};
export async function acceptLanguageRequestInterceptor(request) {
if (!request.headers.has('accept-language')) {
let locale = document.documentElement.lang || 'en';
if (document.documentElement.getAttribute('data-localize-lang')) {
locale = document.documentElement.getAttribute('data-localize-lang') || 'en';
}
request.headers.set('accept-language', locale);
}
return request;
}
/**
* @return {(config: {[key:string]: ?}) => {[key:string]: ?}}
* Creates a request transformer that adds a XSRF header for protecting
* against cross-site request forgery.
* @param {string} cookieName the cookie name
* @param {string} headerName the header name
* @param {Document | { cookie: string }} _document overwriteable for testing
* @returns {RequestInterceptor}
*/
export function cancelPreviousOnNewRequestInterceptorFactory() {
// @ts-ignore axios is untyped so we don't know the type for the source
let prevCancelSource;
return /** @param {{[key:string]: ?}} config */ config => {
// @ts-ignore axios is untyped so we don't know the type for the source
if (prevCancelSource) {
// @ts-ignore
prevCancelSource.cancel('Concurrent requests not allowed.');
export function createXSRFRequestInterceptor(cookieName, headerName, _document = document) {
/**
* @type {RequestInterceptor}
*/
async function xsrfRequestInterceptor(request) {
const xsrfToken = getCookie(cookieName, _document);
if (xsrfToken) {
request.headers.set(headerName, xsrfToken);
}
const source = axios.CancelToken.source();
prevCancelSource = source;
return { ...config, cancelToken: source.token };
};
return request;
}
return xsrfRequestInterceptor;
}
import { expect } from '@open-wc/testing';
import sinon from 'sinon';
import { ajax, setAjax, AjaxClient } from '@lion/ajax';
import { ajax } from '../src/ajax.js';
describe('ajax', () => {
/** @type {import('sinon').SinonFakeServer} */
let server;
beforeEach(() => {
server = sinon.fakeServer.create({ autoRespond: true });
it('exports an instance of AjaxClient', () => {
expect(ajax).to.be.an.instanceOf(AjaxClient);
});
afterEach(() => {
server.restore();
it('can replace ajax with another instance', () => {
const newAjax = new AjaxClient();
setAjax(newAjax);
expect(ajax).to.equal(newAjax);
});
it('interprets Content-Type of the response by default', async () => {
server.respondWith('GET', '/path/to/data/', [
200,
{ 'Content-Type': 'application/json' },
'{ "json": "yes" }',
]);
const response = await ajax.get('/path/to/data/');
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal({ json: 'yes' });
});
it('supports signature (url[, config]) for get(), request(), delete(), head()', async () => {
server.respondWith('data.json', [
200,
{ 'Content-Type': 'application/json' },
'{"success": true}',
]);
const makeRequest = /** @param {string} method */ async method => {
const response = await ajax[method]('data.json', { foo: 'bar' });
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal({ success: true });
};
await Promise.all(['get', 'request', 'delete', 'head'].map(m => makeRequest(m)));
});
it('supports signature (url[, data[, config]]) for post(), put(), patch()', async () => {
server.respondWith('data.json', [
200,
{ 'Content-Type': 'application/json' },
'{"success": true}',
]);
const makeRequest = /** @param {string} method */ async method => {
const response = await ajax[method]('data.json', { data: 'foobar' }, { foo: 'bar' });
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal({ success: true });
};
await Promise.all(['post', 'put', 'patch'].map(m => makeRequest(m)));
});
it('supports GET, POST, PUT, DELETE, REQUEST, PATCH and HEAD methods with XSRF token', async () => {
document.cookie = 'XSRF-TOKEN=test; ';
server.respondWith('data.json', [
200,
{ 'Content-Type': 'application/json' },
'{"success": true}',
]);
const makeRequest = /** @param {string} method */ async method => {
const response = await ajax[method]('data.json');
expect(response.config.headers['X-XSRF-TOKEN']).to.equal('test');
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal({ success: true });
};
await Promise.all(
['get', 'post', 'put', 'delete', 'request', 'patch', 'head'].map(m => makeRequest(m)),
);
});
it('supports GET, POST, PUT, DELETE, REQUEST, PATCH and HEAD methods without XSRF token', async () => {
document.cookie = 'XSRF-TOKEN=; ';
server.respondWith('data.json', [
200,
{ 'Content-Type': 'application/json' },
'{"success": true}',
]);
const makeRequest = /** @param {string} method */ async method => {
const response = await ajax[method]('data.json');
expect(response.config.headers['X-XSRF-TOKEN']).to.equal(undefined);
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal({ success: true });
};
await Promise.all(
['get', 'post', 'put', 'delete', 'request', 'patch', 'head'].map(m => makeRequest(m)),
);
});
it('supports empty responses', async () => {
server.respondWith('GET', 'data.json', [200, { 'Content-Type': 'application/json' }, '']);
const response = await ajax.get('data.json');
expect(response.status).to.equal(200);
expect(response.data).to.equal('');
});
it('supports error responses', async () => {
server.respondWith('GET', 'data.json', [500, { 'Content-Type': 'application/json' }, '']);
try {
await ajax.get('data.json');
throw new Error('error is not handled');
} catch (error) {
expect(error).to.be.an.instanceof(Error);
expect(error.response.status).to.equal(500);
}
});
it('supports non-JSON responses', async () => {
server.respondWith('GET', 'data.txt', [200, { 'Content-Type': 'text/plain' }, 'some text']);
const response = await ajax.get('data.txt');
expect(response.status).to.equal(200);
expect(response.data).to.equal('some text');
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc