UNPKG

2.87 kBPlain TextView Raw
1import {URL} from 'url';
2import httpMocks, {RequestMethod} from 'node-mocks-http';
3import stream from 'stream';
4import Koa, {Context} from 'koa';
5
6import createMockCookies, {MockCookies} from '../create-mock-cookies';
7
8export interface Dictionary<T> {
9 [key: string]: T;
10}
11
12export interface MockContext extends Context {
13 cookies: MockCookies;
14 request: Context['Request'] & {
15 body?: any;
16 session?: any;
17 };
18}
19
20export interface Options<
21 CustomProperties extends Object,
22 RequestBody = undefined
23> {
24 url?: string;
25 method?: RequestMethod;
26 statusCode?: number;
27 session?: Dictionary<any>;
28 headers?: Dictionary<string>;
29 cookies?: Dictionary<string>;
30 state?: Dictionary<any>;
31 encrypted?: boolean;
32 host?: string;
33 requestBody?: RequestBody;
34 throw?: Function;
35 redirect?: Function;
36 customProperties?: CustomProperties;
37}
38
39export default function createContext<
40 CustomProperties,
41 RequestBody = undefined
42>(
43 options: Options<CustomProperties, RequestBody> = {},
44): MockContext & CustomProperties {
45 const app = new Koa();
46
47 const {
48 cookies,
49 method,
50 statusCode,
51 session,
52 requestBody,
53 url = '',
54 host = 'test.com',
55 encrypted = false,
56 throw: throwFn = jest.fn(),
57 redirect = jest.fn(),
58 headers = {},
59 state = {},
60 customProperties = {},
61 } = options;
62
63 const extensions = {
64 ...customProperties,
65 throw: throwFn,
66 session,
67 redirect,
68 state,
69 };
70
71 Object.assign(app.context, extensions);
72
73 const protocolFallback = encrypted ? 'https' : 'http';
74 const urlObject = new URL(url, `${protocolFallback}://${host}`);
75
76 const req = httpMocks.createRequest({
77 url: urlObject.toString(),
78 method,
79 statusCode,
80 session,
81 headers: {
82 // Koa determines protocol based on the `Host` header.
83 Host: urlObject.host,
84 ...headers,
85 },
86 });
87
88 // Some functions we call in the implementations will perform checks for `req.encrypted`, which delegates to the socket.
89 // MockRequest doesn't set a fake socket itself, so we create one here.
90 req.socket = new stream.Duplex() as any;
91 Object.defineProperty(req.socket, 'encrypted', {
92 writable: false,
93 value: urlObject.protocol === 'https:',
94 });
95
96 const res = httpMocks.createResponse();
97 // This is to get around an odd behavior in the `cookies` library, where if `res.set` is defined, it will use an internal
98 // node function to set headers, which results in them being set in the wrong place.
99 // eslint-disable-next-line no-undefined
100 res.set = undefined as any;
101
102 const context = app.createContext(req, res) as MockContext & CustomProperties;
103 context.cookies = createMockCookies(cookies);
104
105 // ctx.request.body is a common enough custom property for middleware to add that it's handy to just support it by default
106 context.request.body = requestBody;
107
108 return context;
109}