Skip to content

Commit cb76f64

Browse files
committed
Misc improvements
- Adds findInDomManyByClass(..) function into jest utils (fix for topcoder-platform#9) - Excludes <Link> from jsx-a11y/anchor-is-valid rule of ESLint (fix for topcoder-platform#10) - A few fixes and enhancements of server/renderer code - Unit tests for server/renderer
1 parent 8f4276f commit cb76f64

File tree

10 files changed

+426
-34
lines changed

10 files changed

+426
-34
lines changed

__tests__/__snapshots__/index.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Object {
3030
"Button": [Function],
3131
"JU": Object {
3232
"findInDomByClass": [Function],
33+
"findInDomManyByClass": [Function],
3334
"render": [Function],
3435
"renderDom": [Function],
3536
"shallowRender": [Function],
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Base rendering of HTML template 1`] = `
4+
"<!DOCTYPE html>
5+
<html>
6+
<head>
7+
8+
9+
<link
10+
href=\\"/test/public/path/main.css\\"
11+
rel=\\"stylesheet\\"
12+
/>
13+
14+
<link rel=\\"shortcut icon\\" href=\\"/favicon.ico\\" />
15+
<meta charset=\\"utf-8\\" />
16+
<meta
17+
content=\\"width=device-width,initial-scale=1.0\\"
18+
name=\\"viewport\\"
19+
/>
20+
</head>
21+
<body>
22+
<div id=\\"react-view\\"></div>
23+
<script id=\\"inj\\" type=\\"application/javascript\\">
24+
window.SPLITS = {}
25+
window.INJ=\\"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDGF1OkTQprU3+cINSXQietFqgqagolJ5j9NlQutv03F1TZonSyrmykHqLzmKxXS0DK59BGs6WiptmyS+pXhsvdE\\"
26+
</script>
27+
<script
28+
src=\\"/test/public/path/polyfills.js\\"
29+
type=\\"application/javascript\\"
30+
></script>
31+
32+
<script
33+
src=\\"/test/public/path/main.js\\"
34+
type=\\"application/javascript\\"
35+
></script>
36+
</body>
37+
</html>"
38+
`;
39+
40+
exports[`Config overriding for injection 1`] = `
41+
"<!DOCTYPE html>
42+
<html>
43+
<head>
44+
45+
46+
<link
47+
href=\\"/test/public/path/main.css\\"
48+
rel=\\"stylesheet\\"
49+
/>
50+
51+
<link rel=\\"shortcut icon\\" href=\\"/favicon.ico\\" />
52+
<meta charset=\\"utf-8\\" />
53+
<meta
54+
content=\\"width=device-width,initial-scale=1.0\\"
55+
name=\\"viewport\\"
56+
/>
57+
</head>
58+
<body>
59+
<div id=\\"react-view\\"></div>
60+
<script id=\\"inj\\" type=\\"application/javascript\\">
61+
window.SPLITS = {}
62+
window.INJ=\\"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDGF1OkTQprU3+cINSXQietFqgqagolJ5j9NlQutv03F1V2aMuxzUDYDD7cyzqAGNMV/1JiJaq6hJDBBPyP0kOQPqRTHIpwZ3UR8U81Nr3rcN1VwLiH9Tq8uEbe/Wfy3thG6aD9S+CX0jMMtbOYD9Pjt\\"
63+
</script>
64+
<script
65+
src=\\"/test/public/path/polyfills.js\\"
66+
type=\\"application/javascript\\"
67+
></script>
68+
69+
<script
70+
src=\\"/test/public/path/main.js\\"
71+
type=\\"application/javascript\\"
72+
></script>
73+
</body>
74+
</html>"
75+
`;
76+
77+
exports[`Hemlet integration works 1`] = `
78+
"<!DOCTYPE html>
79+
<html>
80+
<head>
81+
<title data-react-helmet=\\"true\\">Test Page Title</title>
82+
<meta data-react-helmet=\\"true\\" property=\\"description\\" content=\\"Test Page Description\\"/>
83+
<link
84+
href=\\"/test/public/path/main.css\\"
85+
rel=\\"stylesheet\\"
86+
/>
87+
88+
<link rel=\\"shortcut icon\\" href=\\"/favicon.ico\\" />
89+
<meta charset=\\"utf-8\\" />
90+
<meta
91+
content=\\"width=device-width,initial-scale=1.0\\"
92+
name=\\"viewport\\"
93+
/>
94+
</head>
95+
<body>
96+
<div id=\\"react-view\\"><div data-reactroot=\\"\\"><p>Hello World!</p><p>Goodbye World!</p></div></div>
97+
<script id=\\"inj\\" type=\\"application/javascript\\">
98+
window.SPLITS = {}
99+
window.INJ=\\"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDGF1OkTQprU3+cINSXQietFqgqagolJ5j9NlQutv03F1TZonSyrmykHqLzmKxXS0DK59BGs6WiptmyS+pXhsvdE\\"
100+
</script>
101+
<script
102+
src=\\"/test/public/path/polyfills.js\\"
103+
type=\\"application/javascript\\"
104+
></script>
105+
106+
<script
107+
src=\\"/test/public/path/main.js\\"
108+
type=\\"application/javascript\\"
109+
></script>
110+
</body>
111+
</html>"
112+
`;
113+
114+
exports[`Injection of additional JS scripts 1`] = `
115+
"<!DOCTYPE html>
116+
<html>
117+
<head>
118+
119+
120+
<link
121+
href=\\"/test/public/path/main.css\\"
122+
rel=\\"stylesheet\\"
123+
/>
124+
125+
<link rel=\\"shortcut icon\\" href=\\"/favicon.ico\\" />
126+
<meta charset=\\"utf-8\\" />
127+
<meta
128+
content=\\"width=device-width,initial-scale=1.0\\"
129+
name=\\"viewport\\"
130+
/>
131+
</head>
132+
<body>
133+
<div id=\\"react-view\\"></div>
134+
<script id=\\"inj\\" type=\\"application/javascript\\">
135+
window.SPLITS = {}
136+
window.INJ=\\"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDGF1OkTQprU3+cINSXQietFqgqagolJ5j9NlQutv03F1TZonSyrmykHqLzmKxXS0DK59BGs6WiptmyS+pXhsvdE\\"
137+
</script>
138+
<script
139+
src=\\"/test/public/path/polyfills.js\\"
140+
type=\\"application/javascript\\"
141+
></script>
142+
<script>Dummy JS Sript</script><script>Another Dummy JS Script</script>
143+
<script
144+
src=\\"/test/public/path/main.js\\"
145+
type=\\"application/javascript\\"
146+
></script>
147+
</body>
148+
</html>"
149+
`;
150+
151+
exports[`Server-side rendering (SSR); injection of CSS chunks & Redux state 1`] = `
152+
"<!DOCTYPE html>
153+
<html>
154+
<head>
155+
<title data-react-helmet=\\"true\\"></title>
156+
157+
<link
158+
href=\\"/test/public/path/main.css\\"
159+
rel=\\"stylesheet\\"
160+
/>
161+
<link data-chunk=\\"test-chunk-a\\" href=\\"/test-chunk-a.css\\" rel=\\"stylesheet\\" /><link data-chunk=\\"test-chunk-b\\" href=\\"/test-chunk-b.css\\" rel=\\"stylesheet\\" />
162+
<link rel=\\"shortcut icon\\" href=\\"/favicon.ico\\" />
163+
<meta charset=\\"utf-8\\" />
164+
<meta
165+
content=\\"width=device-width,initial-scale=1.0\\"
166+
name=\\"viewport\\"
167+
/>
168+
</head>
169+
<body>
170+
<div id=\\"react-view\\"><div data-reactroot=\\"\\">Hello Wold!</div></div>
171+
<script id=\\"inj\\" type=\\"application/javascript\\">
172+
window.SPLITS = {}
173+
window.INJ=\\"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDGF1OkTQprU3+cINSXQietFqgqagolJ5j9NlQutv03F1TZonSyrmykHqLzmKxXS0DLPyBqGDNu0Yb+sEvg3s2Nv7ZYfzgAL5LpxryHNGWLiWYlCFgBW1NkdubXDUzuNfddcSwOHmA68IbAvuQnY81RZWKcs/M+w/CFPnw0PiVYASg==\\"
174+
</script>
175+
<script
176+
src=\\"/test/public/path/polyfills.js\\"
177+
type=\\"application/javascript\\"
178+
></script>
179+
180+
<script
181+
src=\\"/test/public/path/main.js\\"
182+
type=\\"application/javascript\\"
183+
></script>
184+
</body>
185+
</html>"
186+
`;
187+
188+
exports[`Setting of response HTTP status the server-side rendering 1`] = `
189+
"HTTP STATUS: 404
190+
<!DOCTYPE html>
191+
<html>
192+
<head>
193+
<title data-react-helmet=\\"true\\"></title>
194+
195+
<link
196+
href=\\"/test/public/path/main.css\\"
197+
rel=\\"stylesheet\\"
198+
/>
199+
200+
<link rel=\\"shortcut icon\\" href=\\"/favicon.ico\\" />
201+
<meta charset=\\"utf-8\\" />
202+
<meta
203+
content=\\"width=device-width,initial-scale=1.0\\"
204+
name=\\"viewport\\"
205+
/>
206+
</head>
207+
<body>
208+
<div id=\\"react-view\\"><div data-reactroot=\\"\\">404 Error Test</div></div>
209+
<script id=\\"inj\\" type=\\"application/javascript\\">
210+
window.SPLITS = {}
211+
window.INJ=\\"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDGF1OkTQprU3+cINSXQietFqgqagolJ5j9NlQutv03F1TZonSyrmykHqLzmKxXS0DK59BGs6WiptmyS+pXhsvdE\\"
212+
</script>
213+
<script
214+
src=\\"/test/public/path/polyfills.js\\"
215+
type=\\"application/javascript\\"
216+
></script>
217+
218+
<script
219+
src=\\"/test/public/path/main.js\\"
220+
type=\\"application/javascript\\"
221+
></script>
222+
</body>
223+
</html>"
224+
`;
225+
226+
exports[`Throws in case of forge.random.getBytes(..) failure 1`] = `[Error: Emulated Failure of forge.random.getBytes(..)]`;

__tests__/server/renderer.jsx

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import _ from 'lodash';
2+
import factory from 'server/renderer';
3+
import fs from 'fs';
4+
import React from 'react';
5+
6+
import { Helmet } from 'react-helmet';
7+
import { Route } from 'react-router-dom';
8+
import { createStore } from 'redux';
9+
10+
let mockFailsForgeRandomGetBytesMethod = false;
11+
12+
jest.mock('node-forge', () => {
13+
const mockForge = require.requireActual('node-forge');
14+
mockForge.random.getBytes = (numBytes, cb) => {
15+
if (mockFailsForgeRandomGetBytesMethod) {
16+
cb(new Error('Emulated Failure of forge.random.getBytes(..)'));
17+
}
18+
let res = '';
19+
for (let i = 0; i < numBytes; i += 1) res += i % 10;
20+
cb(null, res);
21+
};
22+
return mockForge;
23+
});
24+
25+
const TEST_CONTEXT = `${__dirname}/test_data`;
26+
27+
const TEST_REDUX_STATE = {
28+
stateKey1: 'State Value #1',
29+
stateKey2: 'State Value #2',
30+
};
31+
32+
const TEST_HTTP_REQUEST = {
33+
info: 'I am a dummy HTTP request! No need for a complex mock here!',
34+
};
35+
36+
const TEST_WEBPACK_CONFIG = {
37+
context: TEST_CONTEXT,
38+
output: {
39+
publicPath: '/test/public/path/',
40+
},
41+
};
42+
43+
const testBuildInfo =
44+
JSON.parse(fs.readFileSync(`${TEST_CONTEXT}/.build-info`));
45+
46+
beforeAll(() => {
47+
Helmet.canUseDOM = false;
48+
});
49+
50+
afterEach(() => {
51+
delete global.TRU_BUILD_INFO;
52+
mockFailsForgeRandomGetBytesMethod = false;
53+
});
54+
55+
afterAll(() => {
56+
Helmet.canUseDOM = true;
57+
});
58+
59+
/**
60+
* Performs tests with the specified configuration and options.
61+
* @param {Object} webpackConfig
62+
* @param {Object} options
63+
* @return {Promise}
64+
*/
65+
async function coreTest(webpackConfig, options) {
66+
expect(global.TRU_BUILD_INFO).toBeUndefined();
67+
68+
let renderer;
69+
expect(() => {
70+
renderer = factory(webpackConfig, options);
71+
}).not.toThrow();
72+
expect(renderer).toBeInstanceOf(Function);
73+
expect(global.TRU_BUILD_INFO).toEqual(testBuildInfo);
74+
75+
try {
76+
let render = '';
77+
await renderer(_.clone(TEST_HTTP_REQUEST), {
78+
send: (x) => { render += x; },
79+
status: (x) => { render += `HTTP STATUS: ${x}\n`; },
80+
});
81+
expect(render).toMatchSnapshot();
82+
} catch (e) {
83+
expect(e).toMatchSnapshot();
84+
}
85+
}
86+
87+
test('Base rendering of HTML template', () =>
88+
coreTest(TEST_WEBPACK_CONFIG, {}));
89+
90+
test('Config overriding for injection', () =>
91+
coreTest(TEST_WEBPACK_CONFIG, {
92+
beforeRender: async (res, sanitizedConfig) => {
93+
expect(res).toEqual(TEST_HTTP_REQUEST);
94+
expect(sanitizedConfig).toBeInstanceOf(Object);
95+
expect(sanitizedConfig).not.toHaveProperty('SECRET');
96+
return {
97+
configToInject: {
98+
...sanitizedConfig,
99+
additionalKey: 'Additional Value',
100+
},
101+
};
102+
},
103+
}));
104+
105+
test('Hemlet integration works', () =>
106+
coreTest(TEST_WEBPACK_CONFIG, {
107+
Application: () => (
108+
<div>
109+
<p>Hello World!</p>
110+
<Helmet>
111+
<title>Test Page Title</title>
112+
<meta property="description" content="Test Page Description" />
113+
</Helmet>
114+
<p>Goodbye World!</p>
115+
</div>
116+
),
117+
}));
118+
119+
test('Injection of additional JS scripts', () =>
120+
coreTest(TEST_WEBPACK_CONFIG, {
121+
beforeRender: async () => ({
122+
extraScripts: [
123+
'<script>Dummy JS Sript</script>',
124+
'<script>Another Dummy JS Script</script>',
125+
],
126+
}),
127+
}));
128+
129+
test('Server-side rendering (SSR); injection of CSS chunks & Redux state', () =>
130+
coreTest(TEST_WEBPACK_CONFIG, {
131+
Application: () => (
132+
<Route
133+
component={({ staticContext }) => {
134+
staticContext.chunks.push('test-chunk-a');
135+
staticContext.chunks.push('test-chunk-b');
136+
return <div>Hello Wold!</div>;
137+
}}
138+
/>
139+
),
140+
beforeRender: async () => ({
141+
store: createStore(() => _.clone(TEST_REDUX_STATE)),
142+
}),
143+
}));
144+
145+
test('Setting of response HTTP status the server-side rendering', () => {
146+
coreTest(TEST_WEBPACK_CONFIG, {
147+
Application: () => (
148+
<Route
149+
component={({ staticContext }) => {
150+
staticContext.status = 404; // eslint-disable-line no-param-reassign
151+
return <div>404 Error Test</div>;
152+
}}
153+
/>
154+
),
155+
});
156+
});
157+
158+
test('Throws in case of forge.random.getBytes(..) failure', () => {
159+
mockFailsForgeRandomGetBytesMethod = true;
160+
return coreTest(TEST_WEBPACK_CONFIG, {});
161+
});

0 commit comments

Comments
 (0)