Automate UI testing with Mocha and Puppeteer (Updated)

Reading Time: 2 minutes

Github: https://github.com/railsstudent/image-gallery-native-js

  1. yarn add puppeteer mocha chai

    yarn add puppeteer mocha chai
    
  2. Create bootstrap.js file to share global variables among tests. Expose chai.expect, chai.assert and an instance of browser

    'use strict';

const puppeteer = require(‘puppeteer’); const chai = require(‘chai’); const expect = chai.expect; const globalVariables = { browser: global.browser, expect: global.expect };

// puppeteer options const opts = { headless: true, slowMo: 150, timeout: 50000 };

// expose variables before (async function () { global.expect = expect; global.browser = await puppeteer.launch(opts); });

// close browser and reset global variables after (function () { global.browser.close();

global.browser = globalVariables.browser; global.expect = globalVariables.expect; });

  1. Puppeteer creates a headless browser that timeout after 50 seconds and slow down operation by 150 milliseconds.

    // puppeteer options
    const opts = {
    headless: true,
    slowMo: 150,
    timeout: 50000
    };
    
  2. Set up npm script commands in package.json to load the variables in bootstrap.js and execute all UI test cases in test/gallery.test.js directory

    "scripts": {
     "serve:test": "gulp build && http-server dist/ -p 8000",
     "puppeteer": "rm ./test/*.png; SCREEN_SHOT=false mocha test/bootstrap.js test/gallery.test.js",
     "puppeteer-screenshot": "rm ./test/*.png; SCREEN_SHOT=true mocha test/bootstrap.js test/gallery.test.js"
    }
    
  3. Write mocha test case in test file.

    describe('gallery test', function() {
    let page;
    this.timeout(TIMEOUT);

before(async () => { try { page = await browser.newPage(); await page.goto(‘http://localhost:8000/‘); } catch (e) { console.error(e); } });

after(async () => { await page.close(); });

it(‘Shows first image src is not blank and has correct caption and visible buttons’, async () => { try { await page.waitForSelector(‘.image:nth-child(1)’, { visible: true, timeout: 0 }); await page.screenshot( { path: ‘./test/test4.png’ });

  const imageElement = await page.$('.image:nth-child(1)');
  await imageElement.click(); 
  await page.screenshot( {
    path: './test/test5.png'
  });

  const modalHandle = await page.$('.modal.show');
  const imageSrc = await page.evaluate((modal) => 
            modal.querySelector('#modal-image').src, modalHandle);
  const caption = await page.evaluate((modal) => 
            modal.querySelector('#caption').innerText, modalHandle);
  const [btnCloseDisplay, btnCloseOpacity,
    btnLeftDisplay, btnLeftOpacity,
    btnRightDisplay, btnRightOpacity
  ] = await page.evaluate((modal) => { 
    const { opacity: closeOpacity, display: closeDisplay } 
         = window.getComputedStyle(modal.querySelector('.close'));
    const { opacity: leftOpacity, display: leftDisplay } 
         = window.getComputedStyle(modal.querySelector('.left-arrow'));
    const { opacity: rightOpacity, display: rightDisplay } 
         = window.getComputedStyle(modal.querySelector('.right-arrow'));
    return [
      closeDisplay, closeOpacity,  
      leftDisplay, leftOpacity,
      rightDisplay, rightOpacity 
    ];
  }, modalHandle);

  expect(imageSrc).to.not.equal('');
  expect(caption).to.equal(`1 of ${NUM_IMAGES}`);
  expect(btnCloseDisplay).to.equal('inline-block');
  expect(btnRightDisplay).to.equal('inline-block');
  expect(btnLeftDisplay).to.be.equal('block');
  expect(btnCloseOpacity).to.equal('1');
  expect(btnRightOpacity).to.equal('1');
  expect(btnLeftOpacity).to.equal('0');

  const closeHandle = await page.$('.modal.show .close');
  await closeHandle.click();      
  closeHandle.dispose();
  modalHandle.dispose();
} catch (e) {
  console.error(e);
  throw e;
}

}); });

  1. Build development version and serve website at http://localhost:8000

    yarn serve:test
    
  2. Run UI test cases without creating screen shots

    yarn puppeteer
    
  3. Run UI test cases that creates screen shots

    yarn puppeteer-screenshot
    

Finally, we are done.