adonis install @adonisjs/vow-browser
# Skip Chromium download
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true adonis install @adonisjs/vow-browser
You are viewing the legacy version of AdonisJS. Visit https://adonisjs.com for newer docs. This version will receive security patches until the end of 2021.
AdonisJs makes it simpler and easy to write functional tests using the Chrome browser. Under the hood, it makes use of puppeteer to launch a real browser and run assertions.
Since AdonisJs uses Chrome engine, you cannot run your tests on multiple browsers like IE or Firefox. Cross-browser testing is usually done for Frontend Javascript, which is out of the scope of AdonisJs. |
In this guide, we learn about opening a browser programmatically, and running tests like a real user are using the app.
The provider to run browser tests is not shipped by default. Hence we need to pull it from npm
.
Puppeteer comes with a bundled Chromium and takes a while to install it. You can skip the chromium installation by passing PUPPETEER_SKIP_CHROMIUM_DOWNLOAD environment variable.
|
adonis install @adonisjs/vow-browser
# Skip Chromium download
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true adonis install @adonisjs/vow-browser
Next, we need to register the provider under aceProviders
array.
const aceProviders = [
'@adonisjs/vow-browser/providers/VowBrowserProvider'
]
That is all!
Now we have setup the provider; we can start making use of Test/Browser
trait to open a new browser.
Create a new functional test by running the following command.
adonis make:test hello-world
Output
create: test/functional/hello-world.spec.js
'use strict'
const { test, trait } = use('Test/Suite')('Hello World')
trait('Test/Browser')
test('Visit home page', async ({ browser }) => {
const page = await browser.visit('/')
await page.assertHas('Adonis')
})
Now if we run adonis test, hopefully, the test passes ( if you have not changed the default output of / route). Also, let’s talk about how everything works in a nutshell.
Very first we register the Test/Browser
trait, which gives us the access to a browser
object to make HTTP requests.
Next, we visit a URL and access the save the reference to the page object.
Finally, we run assertions to make sure the return HTML does contain Adonis
as text.
If you have used PUPPETEER_SKIP_CHROMIUM_DOWNLOAD
environment variable to install the provider, the chromium is not installed by default, and you are supposed to pass a path to the executable file.
First, make sure to download chromium and place it in a directory, where Node.js can access it.
After that, when making use of the trait
, define the path to the executable file.
trait('Test/Browser', {
executablePath: '/absolute/path/to/chromium'
})
Alternatively, you can define the executable path as an environment variable inside .env.testing
file.
CHROMIUM_PATH=/absolute/path/to/chromium
Below is the list of options you can configure when launching a new browser.
trait('Test/Browser', {
headless: false
})
Key | Description |
---|---|
headless Boolean <true> |
Whether to run tests in headless mode or launch a real browser. |
executablePath String |
Path to the |
slowMo Number |
Pass number of millseconds to be used for slowing down each browser interaction. It can be used to see tests in slow motion. |
dumpio Boolean <false> |
Log all browser console messages to the terminal. |
For all other options, do check puppeteer.launch official documentation.
The most common thing you want to achieve when writing browser tests, is to interact with the webpage. Below is the list of methods for same.
Browser client supports all CSS selectors. Feel free to use your CSS selector skills. |
Type inside an element with given selector.
const { test, trait } = use('Test/Suite')('Hello World')
trait('Test/Browser')
test('Visit home page', async ({ browser }) => {
const page = await browser.visit('/')
await page
.type('[name="username"]', 'virk')
})
To type multiple values, you can chain methods
await page
.type('[name="username"]', 'virk')
.type('[name="age"]', 22)
Select value inside a select box
await page
.select('[name="gender"]', 'Male')
To select multiple options, pass an array of values.
await page
.select('[name="lunch"]', ['Chicken box', 'Salad'])
Select a radio button, based of it’s value
await page
.radio('[name="gender"]', 'Male')
Submit a selected form
await page
.submitForm('form')
// or use a name
await page
.submitForm('form[name="register"]')
Attach one or multiple files
await page
.attach('[name="profile_pic"]', [
Helpers.tmpPath('profile_pic.jpg')
])
Take and save screenshot of the current state of webpage
await page
.type('[name="username"]', 'Virk')
.type('[name="age"]', 27)
.screenshot()
Quite often you have to wait for a certain action to take effect. For example:
Waiting for an element to appear on the webpage.
Waiting for a page to redirect and so on.
Wait for a element to be present inside DOM. The default timeout is to 15 seconds
.
await page
.waitForElement('div.alert')
.assertHasIn('div.alert', 'Success!')
Wait until an element disppears from the DOM.
await page
.waitUntilMissing('div.alert')
.assertNotExists('div.alert')
Wait until page is navigated properly to a new URL.
await page
.click('a[href="/there"]')
.waitForNavigation()
.assertPath('/there')
Wait until the Closure
returns true. The closure is executed in browser context and has access to variables like window
, document
and so on.
await page
.waitFor(function () {
return !!document.querySelector('body.loaded')
})
Below is the list of methods you can use to read the values from the web page.
Get text for a given element or the entire page
await page
.getText()
// or
await page
.getText('span.username')
Get HTML for a given element or entire web page
await page
.getHtml()
// or
await page
.getHtml('div.header')
Find if a given element is visible on page or not.
const isVisible = await page
.isVisible('div.alert')
assert.isFalse(isVisible)
Find if an element exists in DOM.
const hasElement = await page
.hasElement('div.alert')
assert.isFalse(hasElement)
Find if a checkbox is checked
const termsChecked = await page
.isChecked('[name="terms"]')
assert.isTrue(termsChecked)
Get value for a given attribute
const dataTip = await page
.getAttribute('div.tooltip', 'data-tip')
Get all attributes for a given element
const attributes = await page
.getAttributes('div.tooltip')
Get value for a given form element
const value = await page
.getValue('[name="username"]')
assert.equal(value, 'virk')
One way to run assertions is to read the value for certain elements and then run assertions manually. Whereas the browser client bundles a bunch of helper methods to run inline assertions.
Assert the webpage includes the expected text value
await page
.assertHas('Adonis')
Assert a given selector contains the expected value.
await page
.assertHasIn('div.alert', 'Success!')
Assert the value of an attribute is same as expected
await page
.assertAttribute('div.tooltip', 'data-tip', 'Some helpful tooltip')
Assert value for a given form element.
await page
.assertValue('[name="username"]', 'virk')
Assert that checkbox is checked
await page
.assertIsChecked('[name="terms"]')
Assert that checkbox is not checked
await page
.assertIsNotChecked('[name="terms"]')
Assert element is not visible
await page
.assertIsNotVisible('div.notification')
Assert the value of a query param
await page
.assertQueryParam('orderBy', 'id')
Assert that an element exists inside DOM
await page
.assertExists('div.notification')
Assert that an element does not exists inside DOM
await page
.assertNotExists('div.notification')
Assert over the number of elements for a given selector
await page
.assertCount('table tr', 2)
Assert the value of a function executed on a given selector. The fn
is executed in browser context.
await page
.assertEval('table tr', function (el) {
return el.length
}, 2)
In above example, we count the number of tr
inside a table and assert that count is 2
.
Also, you can pass args to the selector fn.
await page
.assertEval(
'div.notification',
function (el, attribute) {
return el[attribute]
},
['id'],
'notification-1'
)
In the above example, we assert over a given attribute of div.notification
. The attribute is dynamic and passed as an argument.
Assert the output of a given function. The fn
is executed in browser context.
The difference between assertFn
and assertEval
is that the later one pre-selects an element before running the function.
await page
.assertFn(function () {
return document.title
}, 'Welcome to Adonis')