- Run tests with Microsoft Edge Chromium locally
- Write a custom reporter
- Use TypeScript modules directly
- Speed up WebDriver tests
- Use Intern programmatically
- Run code before tests start
- Run Intern in my own test page in a browser
- Write tests in an HTML page
- Test ES modules
- Use Intern with a remote service like BrowserStack
- Test non-modular code
- Test non-CORS web APIs
- Run tests with headless Chrome
- Run tests with headless Firefox
- Run tests with Chrome in mobile emulation mode
- Use a custom profile with Firefox
- Ignore global errors or Promise rejections
Microsoft‘s new Chromium-based version of Edge is currently in beta (as of
October 2019), but you can still use it with Intern. Use a browserName
of
"MicrosftEdge"
and a browserVersion
of ."insider preview"
. Intern will
automatically download the latest webdriver for the current beta version of
Edge. If your version of Edge is different (each version of Edge has a
corresponding version of webdriver), you may need to specify a custom driver
version using tunnelOptions
in your intern.json
:
"tunnelOptions": {
"drivers": [
{ "name": "MicrosoftEdgeChromium", "version": "79.0.294.0" }
]
}
Note that the driver name for the Chromium version of Edge is
“MicrosoftEdgeChromium”, even though the browserName
is “MicrosoftEdge”.
See Reporters
In a Node environment, you can use
ts-node to load TypeScript tests and
application modules directly. With ts-node
installed, a test config might look
like:
{
plugins: 'node_modules/ts-node/register/index.js',
suites: 'tests/**/*.ts'
}
Declaring ts-node/register/index.js
as a plugin ensures it’s loaded before any
suites. Once ts-node
is loaded, Intern can load TypeScript modules directly.
⚠️ This method does not work in browsers.
Two features which can have a significant impact on test runtime are code coverage and browser feature tests. Disabling these features can make test debugging and development faster.
- Disable code coverage
- Remove or comment the
coverage
property in a config file, or set it to an empty array orfalse
- Manually disable coverage when running Intern
$ node_modules/.bin/intern coverage=
- Remove or comment the
- Disable browser feature tests
{ environments: { browserName: 'chrome', fixSessionCapabilities: 'no-detect' } }
⚠️ Note that disabling feature tests may lead to test failures, particularly with older or non-standard browsers.
- Load Intern
- In node:
import intern from 'intern';
- In the browser, load the 'browser/intern.js' script
<script src="node_modules/intern/browser/intern.js"></script>
- In node:
- Configure Intern
intern.configure({ suites: ['tests/unit/a.js', 'tests/unit/b.js'], reporters: 'runner' });
- Register for events
intern.on('testStart', test => { console.log(`${test.id} has started`); });
- Run Intern
intern.run();
There several ways to accomplish this:
- If you just need to run some self-contained, synchronous setup code before
testing starts, use a
plugins
script.// setup.js intern.config.suites.push('./some/other/suite.js');
// intern.json { plugins: 'setup.js' }
- If your setup code is still self-contained but needs to do something
asynchronous, you can still load it as a
plugins
script, but use abeforeRun
callback to handle the async code:// setup.js intern.on('beforeRun', () => { return new Promise(resolve => { // async code }); });
- If your startup code needs to load modules using your test loader (one
configured with the loader option), register it as a plugin. These can run
async initialization code in the registerPlugin method, and also have access
to any module loader configured for the tests.
// setup.js const bar = require('./bar'); intern.registerPlugin('foo', () { return bar.getSomething().then(something => { // more async code }); });
// intern.json { plugins: { script: 'setup.js', useLoader: true } }
Load the browser/intern.js
bundle in a page using a script tag. This will
create an intern
global that can be used to configure Intern and start tests.
<!DOCTYPE html>
<html>
<head>
<script src="node_modules/intern/browser/intern.js"></script>
<script>
intern.configure({
suites: ['tests/unit/a.js', 'tests/unit/b.js'],
reporters: 'html'
});
intern.run();
</script>
</head>
<body></body>
</html>
If you’d rather not install Intern, you can load the package from a CDN, like:
<script src="https://unpkg.com/intern@latest/browser/intern.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://unpkg.com/intern@latest/browser/intern.js"></script>
<script>
var registerSuite = intern.getPlugin('interface.object').registerSuite;
registerSuite('app/module', {
test1: function() {
// ...
},
test2: function() {
// ...
}
// ...
});
intern.configure({ reporters: 'html' });
intern.run();
</script>
</head>
<body></body>
</html>
One way to work with ES modules in Node is to install babel-register and load it
as a plugin. This will let Intern load ES modules transparently, without
requiring a build step. Also set the esModules
instrumenter option if code
coverage is desired.
// intern.json
{
node: {
plugins: 'node_modules/babel-register/lib/node.js'
},
instrumenterOptions: {
esModules: true
}
}
The most common way to work with ES modules in the browser is to use a loader that understands ES modules. One option is to use SystemJS configured with babel support:
// intern.json
{
browser: {
loader: {
script: 'systemjs',
options: {
map: {
'plugin-babel': 'node_modules/systemjs-plugin-babel/plugin-babel.js',
'systemjs-babel-build': 'node_modules/systemjs-plugin-babel/systemjs-babel-browser.js'
},
transpiler: 'plugin-babel'
}
}
},
instrumenterOptions: {
esModules: true
}
}
Intern also provides an esm
loader that uses a browser’s native module
support. Internally, modules are loaded using script tags, like:
<script src="myscript.js" type="module"></script>
⚠️ Note that theesm
loader requires that all modules in a dependency tree be ESMs, so its utility is currently somewhat limited.
- Write some unit and/or functional test suites and add them to your
intern.json
{ suites: 'tests/unit/*.js', functionalSuites: 'tests/functional/*.js' }
- Select the desired tunnel in your
intern.json
{ tunnel: 'browserstack' }
- Provide your auth credentials using environment variables or in your
intern.json
or$ export BROWSERSTACK_USERNAME=someone@somedomain.com $ export BROWSERSTACK_ACCESS_KEY=123-456-789
{ tunnelOptions: { username: 'someone@somedomain.com', accessKey: '123-456-789' } }
- Select some environments. Be sure to use the cloud service’s naming
conventions.
{ environments: [ { browserName: 'chrome', version: 'latest', platform: 'MAC' } ] }
- Run Intern
$ node_modules/.bin/intern
Browser code that doesn’t support any module system and expects to be loaded
along with other dependencies in a specific order can be loaded using the
plugins
config option.
{
browser: {
plugins: ['lib/jquery.js', 'lib/plugin.jquery.js']
}
}
Modules specified in the plugins
array will be loaded sequentially in the
order specified.
When writing unit tests with Intern, occasionally you will need to interact with
a web service. However, because the Intern serves code at
http://localhost:9000
by default, any cross-origin requests will fail. In
order to test Ajax requests without using CORS of JSONP, setup a reverse proxy
to Intern and tell the in-browser test runner to load from that URL by setting
the serverUrl
configuration option.
- Set Intern’s
serverUrl
config option to point to the URL of the web server - Set the web server to reverse proxy to
http://localhost:9000
by default - Add
location
directives to pass web service URLs to the web service instead
An nginx config implementing this pattern might look like:
server {
server_name proxy.example;
location /web-service/ {
# This will proxy to http://www.web-service.example/web-service/<rest of url>;
# use `proxy_pass http://www.web-service.example/` to proxy to
# http://www.web-service.example/<rest of url> instead
proxy_pass http://www.web-service.example;
}
location / {
proxy_pass http://localhost:9000;
}
}
- Set Intern’s
serverUrl
config option to point to the URL of the web server - Set the web server to reverse proxy to
http://localhost:9000
for the special/__intern/
location, plus any directories containing JavaScript code
An nginx config implementing this pattern might look like:
server {
server_name proxy.example;
root /var/www/;
location /js/ {
proxy_pass http://localhost:9000;
}
location /__intern/ {
proxy_pass http://localhost:9000;
}
location / {
try_files $uri $uri/ =404;
}
}
Intern interacts with headless Chrome in the same fashion as regular Chrome, it
just has to tell chromedriver to open a headless session. Do this by providing
headless
and disable-gpu
arguments to chromedriver in an environment
descriptor in the test config. You may also want to set the window-size
option
to ensure the browser is large enough to properly render your interface.
{
environments: [
{
browserName: 'chrome',
'goog:chromeOptions': {
args: ['headless', 'disable-gpu', 'window-size=1024,768']
}
}
]
}
Note that when running chrome in a Docker container without a defined user, args
must also include --no-sndbox
.
One of the main benefits of headless Chrome, aside from not having to worry
about window focus, is speed. You can
speed up the testing process further by setting the
fixSessionCapabilities
capability to false
or 'no-detect'
.
Setting up Intern to use Firefox in headless mode is much like setting it up for headless Chrome. Simply provide a 'headless' argument to geckodriver in an environment descriptor in the test config.
{
environments: [
{
browserName: 'firefox',
'moz:firefoxOptions': {
args: ['-headless', '--window-size=1024,768']
}
}
]
}
Intern interacts with Chrome in mobile emulation mode in the same fashion as
regular Chrome, it just has to tell chromedriver to open a mobile emulation
session. Do this by providing a 'mobileEmulation' property in
goog:chromeOptions
in an environment descriptor.
{
environments: [
{
browserName: 'chrome',
'goog:chromeOptions': {
mobileEmulation: {
deviceName: 'Pixel 2'
}
}
}
]
}
Mobile emulation mode may be combined with headless mode, as well.
- Setup a profile in Firefox, create a ZIP archive of the profile directory, and base-64 encode it. The firefox-profile package can help with this.
- Add the profile to the Firefox entry in your
environments
config property. How this is done depends on the version of Firefox in use.- For older versions of Firefox, set a
firefox_profile
property:
{ environments: [ { browserName: 'firefox', firefox_profile: 'UEsDBBQACAAIACynEk...' } ] }
- Starting with geckodriver 0.19, use a
moz:firefoxOptions
property:
{ environments: [ { browserName: 'firefox', 'moz:firefoxOptions': { profile: 'UEsDBBQACAAIACynEk...' } } ] }
- For older versions of Firefox, set a
Intern installs global handlers to catch unhandled exceptions and Promise
rejections. If either of these occur, they're treated as errors and the test run
will fail. However, in some cases these errors may be benign. To treat uncaught
exceptions and unhandled Promise rejections as warnings rather than errors, use
the warnOnUncaughtException and/or warnOnUnhandledRejection config options.
These can be set to true
to ignore all unhandled errors, or to a regular
expression that will be matched against the unhandled rejection reason or error
message.