In test-compose/notesui, create a file named uitest.js containing the following:
const puppeteer = require('puppeteer');
const assert = require('chai').assert;
const util = require('util');
const { URL } = require('url');
describe('Notes', function() {
this.timeout(10000);
let browser;
let page;
before(async function() {
browser = await puppeteer.launch({ slomo: 500 });
page = await browser.newPage();
await page.goto(process.env.NOTES_HOME_URL);
});
after(async function() {
await page.close();
await browser.close();
});
});
This is the start of a Mocha test suite. In the before function, we set up Puppeteer by launching a Puppeteer instance, starting a new Page object, and telling that Page to go to the Notes application home page. That URL is passed in using the named environment variable.
It's useful to first think about scenarios we might want to verify with the Notes applications:
- Log into the Notes application
- Add a note to the application
- View an added note
- Delete an added note
- Log out
- And so on
Here's code for an implementation of the Login scenario:
describe('Login', function() {
before(async function() { ... });
it('should click on login button', async function() {
const btnLogin = await page.waitForSelector('#btnloginlocal');
await btnLogin.click();
});
it('should fill in login form', async function() {
const loginForm = await page.waitForSelector('#notesLoginPage
#notesLoginForm');
await page.type('#notesLoginForm #username', "me");
await page.type('#notesLoginForm #password', "w0rd");
await page.click('#formLoginBtn');
});
it('should return to home page', async function() {
const home = await page.waitForSelector('#notesHomePage');
const btnLogout = await page.waitForSelector('#btnLogout');
const btnAddNote = await page.$('#btnAddNote');
assert.exists(btnAddNote);
});
after(async function() { ... });
});
This test sequence handles the Login Scenario. It shows you a few of the Puppeteer API methods. Documentation of the full API is at https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md. The Page object encapsulates the equivalent of a browser tab in Chrome/Chromium.
The waitForSelector function does what it says – it waits until an HTML element matching the CSS selector appears, and it will wait over one or more page refreshes. There are several variants of this function to allow waiting for several kinds of things. This function returns a Promise, making it worth our time to use async functions in our test code. The Promise will resolve to an ElementHandle, which is a wrapper around an HTML element, or else throw an exception, which would conveniently make the test fail.
The named element, #btnloginlocal, is in partials/header.hbs, and will show up only when a user is not logged in. Hence, we will have determined that the browser is currently displaying a Notes page, and that it is not logged in.
The click method does what it suggests, and causes a mouse button click on the referenced HTML element. If you want to emulate a tap, such as for a mobile device, there is a tap method for that purpose.
The next stage of the test sequence picks up from that click. The browser should have gone to the Login page, and therefore this CSS selector should become valid: #notesLoginPage #notesLoginForm. What we do next is type text for our test user ID and password into the corresponding form elements, and then click on the Log In button.
The next test stage picks up from there, and the browser should be on the home page as determined by this CSS selector: #notesHomePage. If we were logged in successfully, the page should have Log Out (#btnLogout) and ADD Note buttons (#btnAddNote).
In this case, we've used a different function, $, to check if the ADD Note button exists. Unlike the wait functions, $ simply queries the current page without waiting. If the named CSS Selector is not in the current page, it simply returns null rather than throwing an exception. Therefore, to determine that the element exists, we use assert.exists rather than relying on the thrown exception.