{"id":21496959,"url":"https://github.com/financial-times/n-test","last_synced_at":"2025-07-15T19:32:56.550Z","repository":{"id":33287549,"uuid":"117091941","full_name":"Financial-Times/n-test","owner":"Financial-Times","description":"A CLI tool and module for lightweight testing of web applications in browsers, designed for FT.com","archived":false,"fork":false,"pushed_at":"2024-11-16T17:59:43.000Z","size":1294,"stargazers_count":4,"open_issues_count":27,"forks_count":2,"subscribers_count":54,"default_branch":"main","last_synced_at":"2024-11-16T18:30:40.894Z","etag":null,"topics":["component","customer-products","platforms-customer-products"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Financial-Times.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-11T11:35:25.000Z","updated_at":"2024-10-16T10:00:41.000Z","dependencies_parsed_at":"2023-12-26T04:35:45.324Z","dependency_job_id":"c6e5275b-8a8f-4bec-b936-e49a85919587","html_url":"https://github.com/Financial-Times/n-test","commit_stats":{"total_commits":314,"total_committers":30,"mean_commits":"10.466666666666667","dds":"0.42356687898089174","last_synced_commit":"0a3b3d102ee8e18fe1ce196628cd5767d892a6a9"},"previous_names":[],"tags_count":118,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Financial-Times%2Fn-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Financial-Times%2Fn-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Financial-Times%2Fn-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Financial-Times%2Fn-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Financial-Times","download_url":"https://codeload.github.com/Financial-Times/n-test/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226065249,"owners_count":17568179,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["component","customer-products","platforms-customer-products"],"created_at":"2024-11-23T16:20:01.150Z","updated_at":"2025-07-15T19:32:56.542Z","avatar_url":"https://github.com/Financial-Times.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# n-test\nRuns 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.\n\n[![CircleCI](https://circleci.com/gh/Financial-Times/n-test.svg?style=svg\u0026circle-token=d042713e08cb5920c4c2b462e63867d4906a7a66)](https://circleci.com/gh/Financial-Times/n-test)\n[![Node.js version support][shield-node]](#)\n\n[shield-github]: (https://img.shields.io/github/tag/Financial-Times/n-test.svg\n[shield-node]: https://img.shields.io/badge/node.js%20support-\u003e=14.0.0-brightgreen.svg\n\n\n```\nn-test smoke\nn-test smoke --config path/to/config.js --host http://local.ft.com:3002 --header \"X-Api-Key: 1234\"\nn-test smoke basic\nn-test smoke -i\n\nn-test open\nn-test open headers --breakpoint M --config path/to/config.js --host https://local.ft.com:3002\n```\n\nTable of Contents\n-----------------\n  * [Requirements](#requirements)\n  * [Usage](#usage)\n\t  * [Expectations](#expectations)\n\t  * [Request types](#request-types)\n\t  * [FT User Sessions](#ft-user-sessions)\n\t  * [Using Programatically](#using-programatically)\n\t  * [Cross Browser Testing](#cross-browser-testing-experimental)\n  * [Contributing](#contributing)\n\n\nRequirements\n------------\n\nn-test requires the following to run:\n* [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`.\n* [npm][npm] (normally comes with Node.js)\n\n\nUsage\n-----\n\nn-test is easiest to use as a command line tool, installed by npm.\n\n`npm install @financial-times/n-test`\n\nYou 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.\n\n```\nmodule.exports = [\n\t{\n\t\tname: 'basic',\n\t\turls: {\n\t\t\t'/': 200,\n\t\t\t'/redirect': '/'\n\t\t},\n\t\tdescription: 'Test suite descriptions are optional',\n\t}\n];\n```\n\nThen, you can run (assuming your application is running on port 8080 - the default is 3002):\n\n`n-test smoke -H http://localhost:8080`\n\nThis 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.\n\nYou can also run:\n\n`n-test open -H http://localhost:8080`\n\nThis 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.\n\nIf, 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.\n\n### Expectations\n\nChecking 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.\n\n```\n...\nurls: {\n\t'/article/1234': {\n\t\tstatus: 200,\n\t\telements: {\n\t\t\t'.this-should-exist-somewhere': true,\n\t\t\t'.there-should-be-3-of-these': 3,\n\t\t\t'div[exists=false]': false,\n\t\t\t'#should-contain-text': 'text'\n\t\t},\n\t\telementShifts: {\n\t\t\t'.this-should-not-move': { maxCount: 0 },\n\t\t\t'.this-can-move-up-to-3-times': { maxCount: 0 },\n\t\t\t'.this-can-only-move-up-to-100-px': { maxPixels: 100 }\n\t\t},\n\t\tresponseHeaders: {\n\t\t\t'My-Header': 'expected-value'\n\t\t},\n\t\tpageErrors: 0,\n\t\tnetworkRequests: {\n\t\t\t'/some-third-party.js': 1,\n\t\t\t'tracking.pixel': 4, //asserts 4 network requests were made to a URL containing 'tracking.pixel'\n\t\t\t'/will-have-some-of-these.jpg': true,\n\t\t\t'should-not-load-this.js': false\n\t\t},\n\t\tcontent: (content) =\u003e {\n\t\t\treturn content.includes('some-text');\n\t\t},\n\t\tperformance: true, //checks firstPaint/firstContentfulPaint against baseline. default = 2000, or can specify.\n\t\tdescription: 'Each test may have an optional description. It will display when the test result is reported',\n\t}\n}\n...\n```\n\n### Request types\n\nBy default, URLs are assumed to be GET requests, but you can also specify request method/headers/bodies.\n\n```\n...\nurls: {\n\t'/article/1234': {\n\t\theaders: {\n\t\t\t'My-Request-Header': 1\n\t\t}\n\t},\n\t'/post': {\n\t\tbody: { \"some\": \"data\" },\n\t\tmethod: 'POST',\n\t\tstatus: 200,\n\t\thttps: true //Force this URL to be requested over HTTPS, even if the host is not\n\t},\n\t'/wait-for-load': {\n\t\twaitUntil: 'load' //default = domcontentloaded\n\t\telements: {\n\t\t  '.loaded-by-js': true\n\t\t}\n\t}\n}\n...\n```\n\nThese can all be set at a suite level, as well as a URL level, like so:\n\n\n```\n...\n{\n\tname: 'authenticated-requests',\n\theaders: {\n\t\t'api-key': process.env.API_KEY\n\t},\n\turls: {\n\t\t'/article/1': 200,\n\t\t'/article/2': 200,\n\t\t'/article/404': 404\n\t}\n}\n...\n```\n\n### FT User Sessions\n\nTo 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.\n\nFor 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.\n\nThe test output will display the original URL. \n\n*Options:* `premium`, `standard`, `expired`.\n\n*Running locally:*\n\nNgrok 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.\n\nExample steps to run next-article user tests locally:\n\nRun next-article and next-router locally:\n\n```sh\n$ cd ~/next-article\n$ npm start\n$ cd ~/next-router\n$ npm start\n```\n\nRun ngrok on next-article's local port:\n\n```\n$ ./ngrok http 3002\n```\n\nRun the test against the ngrok address provided (can be either http or https):\n\n```\nn-test smoke -H https://05bd2344ebca.ngrok.io\n```\n\n*Remarks*\n\nNeeds 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.\n\n*Example*\n```\n[\n  {\n\tuser: 'premium',\n\turls: [\n\t  '/these-will': 200,\n\t  '/run-with-a': 200,\n\t  '/premium-user': 200\n\t]\n  },\n  {\n\t'user': 'standard',\n\t'urls': [\n\t  '/this-will-run-with-a-standard-user': 200\n\t]\n  },\n  {\n\t'urls': [\n\t  '/these-will-run': 403,\n\t  '/without-session-token': 403\n\t]\n  }\n]\n```\n\n\n### Using Programatically\n\n`n-test` can also be used programatically. This allows you to extend the functionality by adding custom expectations. Below is an example.\n\n```\nconst SmokeTest = require('@financial-times/n-test').SmokeTest;\nconst smoke = new SmokeTests({ headers: { globalHeader: true },  host: 'local.ft.com:3002' });\n\n//Add custom checks like so:\nsmoke.addCheck('custom', async (testPage) =\u003e {\n\tconst metrics = await testPage.page.metrics();\n\n\treturn {\n\t\texpected: `no more than ${testPage.check.custom} DOM nodes`,\n\t\tactual: `${metrics.Nodes} nodes`,\n\t\tresult: testPage.check.custom \u003e= metrics.Nodes\n\t}\n});\n\nsmoke.run()\n\t.then((results) =\u003e { //all passed })\n\t.catch((results) =\u003e { //some failed });\n\nsmoke.run(['basic']);\n```\n\n### Cross Browser Testing [Experimental]\nYou can also run your test suite against Browserstack .\n\nBrowserstack: you must have `BROWSERSTACK_USER` and `BROWSERSTACK_KEY` environment variables set, and enable cross browser tests on a suite/url basis.\n\n*Note* Browserstack supports running off a local host. If your host is local, it will spin up Browserstack Local and proxy through.\n*Caveat* sometimes browserstack local might not clean up properly after itself!\n\n```\n{\n\tname: 'blah'\n\turls: {\n\t\t'/only-puppeteer': {\n\t\t\tstatus: 200\n\t\t},\n\t\t'/no-element-checks': {\n\t\t\tstatus: 200,\n\t\t\tbrowsers: true\n\t\t},\n\t\t'/runs-all-browsers': {\n\t\t\tstatus: 200,\n\t\t\telements: {\n\t\t\t\t'.js-success': true\n\t\t\t},\n\t\t\tbrowsers: true //runs against all enabled browsers, default ['chrome', 'firefox', 'safari', 'internet explorer', 'MicrosoftEdge', 'android'];\n\t\t},\n\t\t'/ios-only': {\n\t\t\tstatus: 200,\n\t\t\telements: {\n\t\t\t\t'.app-install-banner': true\n\t\t\t},\n\t\t\tbrowsers: ['ios']\n\t\t},\n\t}\n}\n```\n\nThe set of enabled browsers to run against can be changed on the command line:\n\n`n-test smoke --browsers \"chrome,internet explorer,android\"`\n\n\n### Cross Browser Screenshotting [Experimental]\n\nThere are two ways to get screenshots generated.\n\n1. As part of your smoke test run, you can generate PNG files with every run:\n\nExample:\n```\n{\n\tname: 'blah'\n\turls: {\n\t\t'/screenshot-me': {\n\t\t\tstatus: 200,\n\t\t\tscreenshot: {\n\t\t\t\tpath: './tmp/screenshots'\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\n2. A command that takes screenshots on multiple browsers, and opens them in a headless chrome window.\n\n`n-test screenshot --browsers ie9,safari`\n\n#### HALPPPPP\n\nCall upon `n-test HALP` to get you through the tough times.\n\nIf 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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinancial-times%2Fn-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffinancial-times%2Fn-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinancial-times%2Fn-test/lists"}