UNPKG

@bbc/spartacus

Version:
220 lines (183 loc) 6.12 kB
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) }); });