@bbc/spartacus
Version:
220 lines (183 loc) • 6.12 kB
JSX
import React from 'react';
import request from 'supertest';
import * as reactDomServer from 'react-dom/server';
import * as styledComponents from 'styled-components';
import { Helmet } from 'react-helmet';
import loadInitialData from '../utilities/loadInitialData';
import renderDocument from './Document';
import ResourceHints from '../helpers/tests/mocks/ResourceHints';
import Document from './Document/component';
import server from './index';
import routes from '../helpers/tests/mocks/routes';
const validateHttpHeader = (headers, headerKey, expectedHeaderValue) => {
const headerKeys = Object.keys(headers);
const headerValues = Object.values(headers);
const indexOfXFrame = headerKeys.indexOf(headerKey);
expect(headerKeys).toContain(headerKey);
expect(headerValues[indexOfXFrame]).toEqual(expectedHeaderValue);
};
jest.mock('react-dom/server', () => ({
renderToString: jest.fn().mockImplementation(() => '<h1>Mock app</h1>'),
renderToStaticMarkup: jest
.fn()
.mockImplementation(() => '<html><body><h1>Mock app</h1></body></html>'),
}));
jest.mock('react-helmet', () => ({
Helmet: {
renderStatic: jest.fn().mockReturnValue({ head: 'tags' }),
},
}));
jest.mock('../utilities/loadInitialData');
styledComponents.ServerStyleSheet = jest.fn().mockImplementation(() => ({
collectStyles: jest.fn().mockReturnValue(<h1>Mock app</h1>),
getStyleElement: jest.fn().mockReturnValue(<style />),
}));
const { ServerStyleSheet } = styledComponents;
describe('Server', () => {
// open a route on the server
server.get('/test-url', async ({ url }, res) => {
const data = await loadInitialData(url, routes);
const { status } = data;
res
.status(status)
.send(
await renderDocument(
url,
data,
routes,
ResourceHints,
ServerStyleSheet,
Helmet,
),
);
});
const makeRequest = async path => request(server).get(path);
describe('/status', () => {
it('should respond with a 200', async () => {
const { statusCode } = await makeRequest('/status');
expect(statusCode).toBe(200);
});
});
describe('Successful render', () => {
const successDataResponse = {
isAmp: false,
data: { some: 'data' },
service: 'someService',
status: 200,
};
const notFoundDataResponse = {
isAmp: false,
data: { some: 'data' },
service: 'someService',
status: 404,
};
describe('200 status code', () => {
beforeEach(() => {
loadInitialData.mockImplementationOnce(() =>
Promise.resolve(successDataResponse),
);
});
it('should respond with rendered data', async () => {
const { text, status } = await makeRequest('/test-url');
expect(status).toBe(200);
expect(reactDomServer.renderToString).toHaveBeenCalledWith(
<h1>Mock app</h1>,
);
expect(reactDomServer.renderToStaticMarkup).toHaveBeenCalledWith(
<Document
app="<h1>Mock app</h1>"
assets={['one.js']}
data={successDataResponse}
helmet={{ head: 'tags' }}
ResourceHints={ResourceHints}
styleTags={<style />}
/>,
);
expect(text).toEqual(
'<!doctype html><html><body><h1>Mock app</h1></body></html>',
);
});
});
// TODO: handle this 404 test better - https://github.com/bbc/SPArtacus/issues/40
describe('404 status code', () => {
beforeEach(() => {
loadInitialData.mockImplementationOnce(() =>
Promise.resolve(notFoundDataResponse),
);
});
it('should respond with a rendered 404', async () => {
const { status, text } = await makeRequest('/some-unknown-url');
expect(status).toBe(404);
expect(text).toEqual(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /some-unknown-url</pre>
</body>
</html>
`);
});
});
});
});
describe('Server HTTP Headers', () => {
let statusRequest;
beforeAll(async () => {
statusRequest = await request(server).get('/status');
});
it(`should not contain 'x-powered-by'`, () => {
const headerKeys = Object.keys(statusRequest.headers);
expect(headerKeys).not.toContain('x-powered-by');
});
it(`should have 'x-frame-options' set to 'DENY'`, () => {
validateHttpHeader(statusRequest.headers, 'x-frame-options', 'DENY');
});
it(`should have X-DNS-Prefetch-Control set to 'off' `, () => {
validateHttpHeader(statusRequest.headers, 'x-dns-prefetch-control', 'off');
});
it(`should have Strict-Transport-Security set to 'max-age=15552000; includeSubDomains' `, () => {
validateHttpHeader(
statusRequest.headers,
'strict-transport-security',
'max-age=15552000; includeSubDomains',
);
});
it(`should have X-Download-Options set to 'noopen' `, () => {
validateHttpHeader(statusRequest.headers, 'x-download-options', 'noopen');
});
it(`should have X-Content-Type-Options set to 'nosniff' `, () => {
validateHttpHeader(
statusRequest.headers,
'x-content-type-options',
'nosniff',
);
});
it(`should have X-XSS-Protection set to '1; mode=block' `, () => {
validateHttpHeader(
statusRequest.headers,
'x-xss-protection',
'1; mode=block',
);
});
describe("should set 'x-clacks-overhead' header", () => {
it('should send the message on', async () => {
validateHttpHeader(
statusRequest.headers,
'x-clacks-overhead',
'GNU Terry Pratchett',
);
});
it('should not log the message', async () => {
global.console.log = jest.fn();
const makeRequest = async path => request(server).get(path);
await makeRequest('/status');
expect(global.console.log).not.toHaveBeenCalledWith(
'GNU Terry Pratchett',
);
});
// It should turn the message around at the end of the line and send it back again (Currently untested)
});
});