Files
remark42/frontend/app/common/fetcher.test.ts
T
Namkhai BandUmputun 98bfc7f5f5 Don't set X-XSRF-TOKEN when the user isn't logged in
An HTTP header cannot be empty, and although some webservers allow this
(nginx, Apache), others answer 400 Bad Request (lighttpd), preventing
the widget from loading.
2021-06-15 02:01:57 -05:00

222 lines
5.8 KiB
TypeScript

jest.mock('./settings', () => ({
siteId: 'remark',
}));
import { RequestError } from 'utils/errorUtils';
import { API_BASE, BASE_URL } from './constants.config';
import { apiFetcher, authFetcher, adminFetcher, JWT_HEADER } from './fetcher';
type FetchImplementaitonProps = {
status?: number;
headers?: Record<string, string>;
json?: () => Promise<unknown>;
text?: () => Promise<unknown>;
data?: unknown;
};
function mockFetch({ headers = {}, data = {}, ...props }: FetchImplementaitonProps = {}) {
window.fetch = jest.fn().mockImplementation(() => {
return {
status: 200,
headers: new Headers(headers),
async json() {
return data;
},
async text() {
return JSON.stringify(data);
},
...props,
};
});
}
describe('fetcher', () => {
const headers = {};
const apiUri = '/anything';
const apiUrl = `${BASE_URL}${API_BASE}/anything?site=remark`;
describe('methods', () => {
it('should send GET request', async () => {
expect.assertions(1);
mockFetch();
await apiFetcher.get(apiUri);
expect(window.fetch).toHaveBeenCalledWith(apiUrl, { method: 'get', headers });
});
it('should send POST request', async () => {
expect.assertions(1);
mockFetch();
await apiFetcher.post(apiUri);
expect(window.fetch).toHaveBeenCalledWith(apiUrl, { method: 'post', headers });
});
it('should send PUT request', async () => {
expect.assertions(1);
mockFetch();
await apiFetcher.put(apiUri);
expect(window.fetch).toHaveBeenCalledWith(apiUrl, { method: 'put', headers });
});
it('should send DELETE request', async () => {
expect.assertions(1);
mockFetch();
await apiFetcher.delete(apiUri);
expect(window.fetch).toHaveBeenCalledWith(apiUrl, { method: 'delete', headers });
});
});
describe('auth fetcher', () => {
it('should use other base url for auth fetcher', async () => {
expect.assertions(1);
mockFetch();
await authFetcher.post(apiUri);
expect(window.fetch).toHaveBeenCalledWith(`${BASE_URL}/auth/anything?site=remark`, { method: 'post', headers });
});
});
describe('admin fetcher', () => {
it('should use other base url for auth fetcher', async () => {
expect.assertions(1);
mockFetch();
await adminFetcher.post(apiUri);
expect(window.fetch).toHaveBeenCalledWith(`${BASE_URL}${API_BASE}/admin/anything?site=remark`, {
method: 'post',
headers,
});
});
});
describe('headers', () => {
it('should set active token and than clean it on unauthorized respose', async () => {
expect.assertions(4);
const headersWithJwt = { [JWT_HEADER]: 'token', ...headers };
// Set token to `activeJwtToken`
mockFetch({ headers: headersWithJwt });
await apiFetcher.get(apiUri);
expect(window.fetch).toHaveBeenCalled();
// Check if `activeJwtToken` saved and clean
mockFetch({ headers, status: 401 });
await expect(
apiFetcher.get(apiUri).then(() => {
throw Error('apiFether shoud throw error on 401 responce');
})
).rejects.toEqual(new Error('Not authorized.'));
expect(window.fetch).toHaveBeenCalledWith(apiUrl, {
method: 'get',
headers: headersWithJwt,
});
// Check if `activeJwtToken` was cleaned
mockFetch({ headers });
await apiFetcher.get(apiUri);
expect(window.fetch).toHaveBeenCalledWith(apiUrl, { method: 'get', headers });
});
});
describe('send data', () => {
it('should send JSON', async () => {
expect.assertions(1);
const data = { text: 'text' };
const headersWithContentType = { ...headers, 'Content-Type': 'application/json' };
mockFetch();
await apiFetcher.post(apiUri, {}, data);
expect(window.fetch).toBeCalledWith(apiUrl, {
method: 'post',
headers: headersWithContentType,
body: JSON.stringify(data),
});
});
it("shouldn't send content-type with form data", async () => {
expect.assertions(1);
const body = new FormData();
mockFetch();
await apiFetcher.post(apiUri, {}, body);
expect(window.fetch).toHaveBeenCalledWith(apiUrl, {
method: 'post',
body,
headers,
});
});
});
describe('request errors', () => {
it('should throw json on api json response with >= 400 status code', async () => {
expect.assertions(1);
const data = {
code: 2,
error: 'you just cant',
details: 'you just cant at all',
};
mockFetch({ status: 400, data });
await expect(apiFetcher.get(apiUri)).rejects.toEqual(data);
});
it('should throw error on api json response with >= 400 status code and bad json from server', async () => {
expect.assertions(1);
const data = ']{{"code: 2';
mockFetch({ status: 400, data });
await expect(apiFetcher.get(apiUri)).rejects.toEqual(data);
});
it('should throw special error object on 401 status', async () => {
expect.assertions(1);
const response = '<html>unauthorized nginx response</html>';
mockFetch({
status: 401,
json() {
throw new Error('json parse error');
},
async text() {
return response;
},
});
await expect(apiFetcher.get(apiUri)).rejects.toEqual(new RequestError('Not authorized.', 401));
});
it('should throw "Something went wrong." object on unknown status', async () => {
expect.assertions(1);
mockFetch({
status: 400,
json() {
throw new Error('json parse error');
},
async text() {
return 'you given me something wrong';
},
});
await expect(apiFetcher.get(apiUri)).rejects.toEqual(new RequestError('Something went wrong.', 0));
});
});
});