1 | # n-test
|
2 | Runs smoke tests with Puppeteer (and optionally Browserstack). Define a set of URLs and expected behaviour in JSON, without the toil of writing full blown tests.
|
3 |
|
4 | [![CircleCI](https://circleci.com/gh/Financial-Times/n-test.svg?style=svg&circle-token=d042713e08cb5920c4c2b462e63867d4906a7a66)](https://circleci.com/gh/Financial-Times/n-test)
|
5 | [![Node.js version support][shield-node]](#)
|
6 |
|
7 | [shield-github]: (https://img.shields.io/github/tag/Financial-Times/n-test.svg
|
8 | [shield-node]: https://img.shields.io/badge/node.js%20support->=14.0.0-brightgreen.svg
|
9 |
|
10 |
|
11 | ```
|
12 | n-test smoke
|
13 | n-test smoke --config path/to/config.js --host http://local.ft.com:3002 --header "X-Api-Key: 1234"
|
14 | n-test smoke basic
|
15 | n-test smoke -i
|
16 |
|
17 | n-test open
|
18 | n-test open headers --breakpoint M --config path/to/config.js --host https://local.ft.com:3002
|
19 | ```
|
20 |
|
21 | Table of Contents
|
22 | -----------------
|
23 | * [Requirements](#requirements)
|
24 | * [Usage](#usage)
|
25 | * [Expectations](#expectations)
|
26 | * [Request types](#request-types)
|
27 | * [FT User Sessions](#ft-user-sessions)
|
28 | * [Using Programatically](#using-programatically)
|
29 | * [Cross Browser Testing](#cross-browser-testing-experimental)
|
30 | * [Contributing](#contributing)
|
31 |
|
32 |
|
33 | Requirements
|
34 | ------------
|
35 |
|
36 | n-test requires the following to run:
|
37 | * [Node.js][node] Version defined by `engines.node` in `package.json`. Run command `nvm use` to switch your local Node version to the one specified in `.nvmrc`.
|
38 | * [npm][npm] (normally comes with Node.js)
|
39 |
|
40 |
|
41 | Usage
|
42 | -----
|
43 |
|
44 | n-test is easiest to use as a command line tool, installed by npm.
|
45 |
|
46 | `npm install @financial-times/n-test`
|
47 |
|
48 | You must create a _config file_ containing the set of URLs to test. This will be a javascript file, that exports an array of test suites. The default location is `test/smoke.js`. This can be overriden with a command line parameter.
|
49 |
|
50 | ```
|
51 | module.exports = [
|
52 | {
|
53 | name: 'basic',
|
54 | urls: {
|
55 | '/': 200,
|
56 | '/redirect': '/'
|
57 | },
|
58 | description: 'Test suite descriptions are optional',
|
59 | }
|
60 | ];
|
61 | ```
|
62 |
|
63 | Then, you can run (assuming your application is running on port 8080 - the default is 3002):
|
64 |
|
65 | `n-test smoke -H http://localhost:8080`
|
66 |
|
67 | This will run a headless browser, open the URLs and check (in the above case) the response status is 200 for / and '/redirect' redirects to '/'. If both of those things are true, the command will exit with a success status.
|
68 |
|
69 | You can also run:
|
70 |
|
71 | `n-test open -H http://localhost:8080`
|
72 |
|
73 | This allows you to select a suite of URLs (in this case, "basic"), and open them in Chromium. This is useful for manually testing a set of URLs.
|
74 |
|
75 | If, when running locally, you are seeing errors about certificates not being valid, set NODE_ENV to be 'development' e.g. `NODE_ENV=development;n-test smoke -H http://localhost:8080`. This will use some launch options that ignore certificate errors.
|
76 |
|
77 | ### Expectations
|
78 |
|
79 | Checking response statii is great for checking that your application responds with _something_, but not necessarily the right thing. n-test comes with a bunch of basic things that you check for.
|
80 |
|
81 | ```
|
82 | ...
|
83 | urls: {
|
84 | '/article/1234': {
|
85 | status: 200,
|
86 | elements: {
|
87 | '.this-should-exist-somewhere': true,
|
88 | '.there-should-be-3-of-these': 3,
|
89 | 'div[exists=false]': false,
|
90 | '#should-contain-text': 'text'
|
91 | },
|
92 | elementShifts: {
|
93 | '.this-should-not-move': { maxCount: 0 },
|
94 | '.this-can-move-up-to-3-times': { maxCount: 0 },
|
95 | '.this-can-only-move-up-to-100-px': { maxPixels: 100 }
|
96 | },
|
97 | responseHeaders: {
|
98 | 'My-Header': 'expected-value'
|
99 | },
|
100 | pageErrors: 0,
|
101 | networkRequests: {
|
102 | '/some-third-party.js': 1,
|
103 | 'tracking.pixel': 4, //asserts 4 network requests were made to a URL containing 'tracking.pixel'
|
104 | '/will-have-some-of-these.jpg': true,
|
105 | 'should-not-load-this.js': false
|
106 | },
|
107 | content: (content) => {
|
108 | return content.includes('some-text');
|
109 | },
|
110 | visibleContent: {
|
111 | contentSelector: '.headline, .image, .standfirst'
|
112 | threshold: 30 // % of viewport that should be visible content
|
113 | },
|
114 | performance: true, //checks firstPaint/firstContentfulPaint against baseline. default = 2000, or can specify.
|
115 | description: 'Each test may have an optional description. It will display when the test result is reported',
|
116 | }
|
117 | }
|
118 | ...
|
119 | ```
|
120 |
|
121 | ### Request types
|
122 |
|
123 | By default, URLs are assumed to be GET requests, but you can also specify request method/headers/bodies.
|
124 |
|
125 | ```
|
126 | ...
|
127 | urls: {
|
128 | '/article/1234': {
|
129 | headers: {
|
130 | 'My-Request-Header': 1
|
131 | }
|
132 | },
|
133 | '/post': {
|
134 | body: { "some": "data" },
|
135 | method: 'POST',
|
136 | status: 200,
|
137 | https: true //Force this URL to be requested over HTTPS, even if the host is not
|
138 | },
|
139 | '/wait-for-load': {
|
140 | waitUntil: 'load' //default = domcontentloaded
|
141 | elements: {
|
142 | '.loaded-by-js': true
|
143 | }
|
144 | }
|
145 | }
|
146 | ...
|
147 | ```
|
148 |
|
149 | These can all be set at a suite level, as well as a URL level, like so:
|
150 |
|
151 |
|
152 | ```
|
153 | ...
|
154 | {
|
155 | name: 'authenticated-requests',
|
156 | headers: {
|
157 | 'api-key': process.env.API_KEY
|
158 | },
|
159 | urls: {
|
160 | '/article/1': 200,
|
161 | '/article/2': 200,
|
162 | '/article/404': 404
|
163 | }
|
164 | }
|
165 | ...
|
166 | ```
|
167 | ### Actions
|
168 |
|
169 | n-test allows some basic actions (e.g. clicking, interacting with forms). This has been ported from [pa11y](https://github.com/pa11y/pa11y) - see the [section on their README](https://github.com/pa11y/pa11y/blob/5.0.4/README.md#actions) for more.
|
170 |
|
171 | ### FT User Sessions
|
172 |
|
173 | To run a test suite for a type of FT subscriber, add a `user` property to the suite and it will set the session tokens for that type of user before running the tests in that suite.
|
174 |
|
175 | For the test to get the user session tokens from [`next-test-sessions-lambda`](http://github.com/financial-times/next-test-sessions-lambda), it needs to rewrite the URL being tested to an ft.com host. The original URL is set in the `FT-Test-Host` header, which tells `next-router` to proxy the test URL rather than production.
|
176 |
|
177 | The test output will display the original URL.
|
178 |
|
179 | *Options:* `premium`, `standard`, `expired`.
|
180 |
|
181 | *Running locally:*
|
182 |
|
183 | Ngrok provides a secure public URL for the local test app and will need to be installed and running on the the app's port. Tests will then use the TEST_URL variable to specify the ngrok URL when starting the service. The local `next-router` needs to be running, as it will be used to proxy the test URL.
|
184 |
|
185 | Example steps to run next-article user tests locally:
|
186 |
|
187 | Run next-article and next-router locally:
|
188 |
|
189 | ```sh
|
190 | $ cd ~/next-article
|
191 | $ make run
|
192 | $ cd ~/next-router
|
193 | 4 make run
|
194 | ```
|
195 |
|
196 | Run ngrok on next-article's local port:
|
197 |
|
198 | ```
|
199 | $ ./ngrok http 3002
|
200 | ```
|
201 |
|
202 | Run the test against the ngrok address provided (can be either http or https):
|
203 |
|
204 | ```
|
205 | make smoke TEST_URL=https://05bd2344ebca.ngrok.io
|
206 | ```
|
207 |
|
208 | *Remarks*
|
209 |
|
210 | Needs to set TEST_SESSIONS_URL (url to [`next-test-sessions-lambda`](http://github.com/financial-times/next-test-sessions-lambda)) and TEST_SESSIONS_API_KEY environment variables when running the tests.
|
211 |
|
212 | *Example*
|
213 | ```
|
214 | [
|
215 | {
|
216 | user: 'premium',
|
217 | urls: [
|
218 | '/these-will': 200,
|
219 | '/run-with-a': 200,
|
220 | '/premium-user': 200
|
221 | ]
|
222 | },
|
223 | {
|
224 | 'user': 'standard',
|
225 | 'urls': [
|
226 | '/this-will-run-with-a-standard-user': 200
|
227 | ]
|
228 | },
|
229 | {
|
230 | 'urls': [
|
231 | '/these-will-run': 403,
|
232 | '/without-session-token': 403
|
233 | ]
|
234 | }
|
235 | ]
|
236 | ```
|
237 |
|
238 |
|
239 | ### Using Programatically
|
240 |
|
241 | `n-test` can also be used programatically. This allows you to extend the functionality by adding custom expectations. Below is an example.
|
242 |
|
243 | ```
|
244 | const SmokeTest = require('@financial-times/n-test').SmokeTest;
|
245 | const smoke = new SmokeTests({ headers: { globalHeader: true }, host: 'local.ft.com:3002' });
|
246 |
|
247 | //Add custom checks like so:
|
248 | smoke.addCheck('custom', async (testPage) => {
|
249 | const metrics = await testPage.page.metrics();
|
250 |
|
251 | return {
|
252 | expected: `no more than ${testPage.check.custom} DOM nodes`,
|
253 | actual: `${metrics.Nodes} nodes`,
|
254 | result: testPage.check.custom >= metrics.Nodes
|
255 | }
|
256 | });
|
257 |
|
258 | smoke.run()
|
259 | .then((results) => { //all passed })
|
260 | .catch((results) => { //some failed });
|
261 |
|
262 | smoke.run(['basic']);
|
263 | ```
|
264 |
|
265 | ### Cross Browser Testing [Experimental]
|
266 | You can also run your test suite against Browserstack .
|
267 |
|
268 | Browserstack: you must have `BROWSERSTACK_USER` and `BROWSERSTACK_KEY` environment variables set, and enable cross browser tests on a suite/url basis.
|
269 |
|
270 | *Note* Browserstack supports running off a local host. If your host is local, it will spin up Browserstack Local and proxy through.
|
271 | *Caveat* sometimes browserstack local might not clean up properly after itself!
|
272 |
|
273 | ```
|
274 | {
|
275 | name: 'blah'
|
276 | urls: {
|
277 | '/only-puppeteer': {
|
278 | status: 200
|
279 | },
|
280 | '/no-element-checks': {
|
281 | status: 200,
|
282 | browsers: true
|
283 | },
|
284 | '/runs-all-browsers': {
|
285 | status: 200,
|
286 | elements: {
|
287 | '.js-success': true
|
288 | },
|
289 | browsers: true //runs against all enabled browsers, default ['chrome', 'firefox', 'safari', 'internet explorer', 'MicrosoftEdge', 'android'];
|
290 | },
|
291 | '/ios-only': {
|
292 | status: 200,
|
293 | elements: {
|
294 | '.app-install-banner': true
|
295 | },
|
296 | browsers: ['ios']
|
297 | },
|
298 | }
|
299 | }
|
300 | ```
|
301 |
|
302 | The set of enabled browsers to run against can be changed on the command line:
|
303 |
|
304 | `n-test smoke --browsers "chrome,internet explorer,android"`
|
305 |
|
306 |
|
307 | ### Cross Browser Screenshotting [Experimental]
|
308 |
|
309 | There are two ways to get screenshots generated.
|
310 |
|
311 | 1. As part of your smoke test run, you can generate PNG files with every run:
|
312 |
|
313 | Example:
|
314 | ```
|
315 | {
|
316 | name: 'blah'
|
317 | urls: {
|
318 | '/screenshot-me': {
|
319 | status: 200,
|
320 | screenshot: {
|
321 | path: './tmp/screenshots'
|
322 | }
|
323 | }
|
324 | }
|
325 | }
|
326 | ```
|
327 |
|
328 | 2. A command that takes screenshots on multiple browsers, and opens them in a headless chrome window.
|
329 |
|
330 | `n-test screenshot --browsers ie9,safari`
|
331 |
|
332 | #### HALPPPPP
|
333 |
|
334 | Call upon `n-test HALP` to get you through the tough times.
|
335 |
|
336 | If a test is failing because a subscription has expired, e.g. the premium subscription on nextpremium@ftqa.org has expired, email customer support to renew it.
|