no-bro-cote
v0.2.19
Published
Node Browser Code testing. Run unit tests on your JavaScript code for the browser.
Downloads
25
Maintainers
Readme
NoBroCote
Node Browser Code testing. Run unit tests on your JavaScript code for the browser.
NoBroCote is designed for the automation of testing JavaScript code which is made for the browser. It provides methods to run the code units inside of a headless browser via Puppeteer. Unlike many of the big players in code testing it is not a swiss army knife for every purpose, but particularly to test the code designed for the browser via node, without having to open a browser, and also without writing the test environment from scratch every time.
No big learning curve involved! A Unit takes a name, an expected result and a function, which is the test, that's all.
Use case
This software is designed to test if an application for the browser is correctly working.
- Is it loading into the HTML page?
- Is it creating the required DOM elements?
- Is it responding?
- Is it serving the correct output?
- etc.
It is not suitable for UI tests, reacting to (touch) input and anything like that, or testing if the code is running consistent over different browsers.
Installation
NoBroCote is made for unit tests with node.js, therefore a installation via npm is advisable. As it is most likely is only needed for testing with the --save-dev
flag.
npm install no-brote-cote --save-dev
Usage
The first step is to create a new .js
file (most likely in your test folder), import a test
-instance, create one ore more a test units, finally initialize.
(Psst. No 'time' for reading? Jump straight to a basic sample code.)
To run the test(s), you can use the CLI.
Importing
Inside of your js-file, import a test
-instance.
import { test } from "no-bro-code";
Creating Test Units
Now it is time to create a test unit. A unit takes:
name
<string> Unit Nameexpect
<*> Expected resultfn
<Function> The actual test. A function for testing.fnArgs
<...any> Optional parameters for the function.
The function has access to the html page. It acts like a single function you would execute in a script tag. It has access to all scripts and modules passed via addScript
or addImport
. The function can be asynchronous or not. It must return something which can be compared with the expected result.
Example:
test.makeUnit(
"my first unit",
"hello",
(greeting) => {
document.body.textContent = greeting;
return document.body.textContent;
},
"hello"
);
Controlling the Test Assertion
The regular assertion compares the expected value and the result for equality without type conversion (===). If this is not the desired behavior, there are some operators available to control the assertion process. Operators are activated by passing them to the expect parameter of a makeUnit
method.
Available operators are:
!|
not!=|
not, with type conversion||
or, values can be separated with a '|', like this: ||valueA|valueB|valueC==|
equality, with type conversion
Examples:
test.makeUnit(
"notTest",
"!|cat",
() => {
document.body.textContent = "dog";
return document.body.textContent;
}
);
test.makeUnit(
"orTest",
"||cat|dog|bird",
() => {
const pickPet = () => ["cat", "dog", "bird"].at(Math.floor(Math.random()*3));
document.body.textContent = pickPet();
return document.body.textContent;
}
);
Controlling Errors
Sometimes it is necessary to test if an error is thrown. Similar to the just featured operators there are operators for errors (those keywords are also passed to the expect parameter). _Those are:
e|
(for allowing all errors)e|EvalError
e|InternalError
e|RangeError
e|ReferenceError
e|SyntaxError
e|TypeError
e|URIError
Example:
test.makeUnit(
"expectErrorTest",
"e|TypeError",
() => {
throw new TypeError("I am glad this error was raised!");
}
);
Custom Errors
Custom errors can be also be added to error list:
// first create the error (or import it from somewhere else)
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
// second append it to the error list
test.errorList.push(ValidationError.name);
// now it can be used with: 'e|ValidationError'
Expecting a Failure
Assuming a situation, where an error should be raised, but the test should not fail. It is possible to anyhow count this as a success. With the setting:
test.expectFailure = true;
- at least one unit has to fail.
Importing Scripts and Modules
addScript
This method appends a script tag to the html page. The method requires an object which can have the following keys (as defined by Puppeteer):
url
<string> URL of a script to be added.path
<string> Path to the JavaScript file to be injected into frame. If path is a relative path, then it is resolved relative to projects root directory (cwd).content
<string> Raw JavaScript content to be injected into frame.type
<string> Script type. Use 'module' in order to load a Javascript ES6 module.
You can also pass an array of objects. An example may look like so:
test.addScript({
path: "./path/to/script"
});
Also see: devdocs.io/puppeteer.
addImport
This method provides ES6 imports for the test runner. In contrast to addScripts
, this method needs a valid ES6 import statement as a string. These imports are directly accessible by the test units as they are part of one script tag in the browser.
- Relative imports are resolved relative to the projects root directory (cwd).
- Multiple imports can be passed as an array. A possible example may be as follows:
test.addImport('import myModule from "./path/to/module"');
HTML Page
By default a very skeleton of an HTML page is getting opened, on which the tests are getting injected. But also it is possible to load a custom HTML page. The page has to be reachable from the projects root folder and its path has to be declared relative to root. Assuming a HTML page with the path ./test/fixtures/page.html
the declaration inside of the test file may look as follows:
test.htmlPage = "./test/fixtures/page.html";
Server Port
By default the server runs on port 10000
. If the port is already in use the port number is raised by one until an open port was found. The initial port can be changed, if this is a bad starting point for any reason. Simply declare another port in that case.
test.port = 8080;
Initializing the tests
After all imports are done and all units are declared, all that is left to do is to set the following line at the end of the file:
test.init();
The js file could now be called with node from the projects root directory:
node ./path/to/file.js
Much more convenient is to use the CLI, by adding it to the test section of your package.json
"scripts": {
"test": "no-bo-cote",
}
Basic Sample Code
import { test } from "no-bro-cote";
// imports (optional)
test.addScript({
path: "./path/to/script"
});
test.addImport('import myModule from "./path/to/module"');
// units
test.makeUnit(
"myFirstUnit",
"hello",
() => {
document.body.textContent = "hello";
return document.body.textContent;
}
);
test.makeUnit(
"myNotUnit",
"!|hello",
async (argExample) => {
document.body.textContent = argExample;
return document.body.textContent;
},
"world"
);
test.makeUnit(
"myExpectErrorUnit",
"e|ReferenceError",
() => {
const thisIsPython = False;
}
);
// last step -> the file is now callable from the projects root directory and by the CLI
test.init();
CLI
The CLI is for the most part inspired/adopted from the great AVA test runner.
no-bro-cote [<pattern>...]
Positionals:
pattern Select which test files to run. Leave empty if you want to run all
test files as per your configuration. Accepts glob and minimatch
patterns, directories that (recursively) contain test files, and
relative or absolute file paths. [string]
Options:
--help Show help [boolean]
--version Show version number [boolean]
--debug Enable debug mode [boolean]
--fail-fast Stop after first test failure [boolean]
--ignore-coherence Skip test for a coherent test file [boolean]
-s, --serial Run tests serially [boolean]
Examples:
no-bro-cote
no-bro-cote test.js
no-bro-cote ./test/
no-bro-cote "**/**test.js"
If no patterns are provided no-bro-cote searches for test files using the following patterns:
test.js
src/test.js
source/test.js
**/test-*.js
**/*.spec.js
**/*.test.js
**/test/**/*.js
**/tests/**/*.js
**/__tests__/**/*.js
Files inside node_modules
and files inside directories starting with .git
are always ignored. So are files starting with _
or inside of directories that start with a single _
. Additionally, files matching these patterns are ignored by default, unless different patterns are configured:
**/__tests__/**/__helper__/**/*
**/__tests__/**/__helpers__/**/*
**/__tests__/**/__fixture__/**/*
**/__tests__/**/__fixtures__/**/*
**/test/**/helper/**/*
**/test/**/helpers/**/*
**/test/**/fixture/**/*
**/test/**/fixtures/**/*
**/tests/**/helper/**/*
**/tests/**/helpers/**/*
**/tests/**/fixture/**/*
**/tests/**/fixtures/**/*
When using npm test
, you can pass positional arguments directly npm test test2.js
, but flags needs to be passed like npm test -- --debug
.
Configuration
All of the CLI options can be configured in the no-bro-cote
section of your package.json
file. This allows you to modify the default behavior of the no-bro-cote
command, so you don't have to repeatedly type the same options on the command prompt.
To ignore files, prefix the pattern with an !
(exclamation mark).
package.json
:
{
"no-bro-cote": {
"debug": false,
"extensions": [
"js",
"mjs"
],
"failFast": true,
"files": [
"test/**/*",
"!test/exclude-files-in-this-directory",
"!**/exclude-files-with-this-name.*"
],
"ignoreCoherence": false,
"serial": true
}
}
Arguments passed to the CLI will always take precedence over the CLI options configured in package.json
.
Options
debug
: enables debug mode if set to true. Let's no-bro-cote run the tests serially and provides verbose console outputextensions
: extensions of test files. Setting this option overrides the default["mjs", "js"]
valuefailFast
: stop running further tests once a test failsfiles
: an array of glob patterns to select test files. Files with an underscore prefix are ignored. By default only selects files withmjs
&js
extensions, even if the pattern matches other files. Specifyextensions
to allow other file extensionsignoreCoherence
: Every test file is getting executed as a subprocess. To prevent the CLI to run every js-file, that is found, the source code is getting analyzed if it is a no-bro-cote test file. Setting this option to true, disables this check (which might be dangerous).serial
: By default the tests are running concurrently. Setting this option to true, only one test file runs at a time.
Note that all arguments provided on the CLI overrides the options configured in package.json
.
License
This work is licensed under GPL-3.0.
....
..;ddc...
..:do,,cdl'..
..:do,'...':do'..
...cdo,'.......':do;..
..'ldl,............';od:..
..'ldc,................',odc...
..'ld:'.....................,ldl'..
..'oxc'..pq....po.....,ooOoo,...':do'..
..,od:,....XWXc..kN'...c0k'''x0l....':do,..
..,dd;'......X0cXx.kN'..,Wx.....xW;.....';dd;..
..;dd;'........X0..O0ON'..'X0'...'0X,.......',odc..
..;dd;'..........X0...o0N'..."okdggko"...........,ldc...
..:do,'..............................................,ldl'..
..:xo,'..................................................':xo,..
..cdo,'.k00000000Oxo,.....c00000000Oko;........:dOKXNNXKOd:..';dd;..
...cdl,....KMMMNOOOXMMMW:....cMMMM000XMMMMl.....;KMMMW0kkONMMMK;..',dd;..
..'ldc'......KMMM....'MMMMd....cMMMM....0MMM0....;WMMMO'....'kMMMW;...',odc...
..'lxc'........KMMMNOOOXMMWO'....cMMMM...oNMMWc....OMMMM'.......NMMMO......'cdl'..
..'odc,..........KMMMW00KXMMW0c....cMMMMMMMMMM0,.....0MMMW........XMMMK........'cdo'..
...lxc'...........KMMP.....oMMMM,...cMMMM::lOMMMX:....oMMMMl......;MMMMl........,ldc...
..'lxl,.........KMMM....,KMMMW'...cMMMM'...dMMMWc....xMMMMk....xWMMMd.......,lx:..
..'cdo,.......KMMMMMMMMMWXk;....cMMMM'....oMMMMo....ikXMMMMMMMMXkj.....',odc..
..;dd;'....kooooooooou*'.....cgggg......:uoOX.......*MMMMMM*......,dd;..
..;od;'.......................................................',dd;..
..,dx:'...................................................';dd;..
..'ldc'........;dkXX:..;dkxkl..MXXXXXM..MXXXM.........':do,..
...cxl,.....:MX,....,WK'.oMx...cMd....MXL,,.......';xo'..
..cdd,'..:MK,....;WK'.lMx...cMd....MXP"".....':do'..
..;dd,'.;xOXX:..;xOkOl....cOd....MXXXM...,cxo'..
..,od;'..............................'cdl'..
..'oxc,..........................'cxc...
..'ldl'......................,odl...
...cxl,..................,od:...
..:do;'.............,od:..
..,od:'........';dd;..
..'ox:'....';dd,..
..'ldc'';do,..
...ldxo'..
...o..
.