Slide 1

Slide 1 text

Stack-Agnostic End-to-End Testing with Jest and Puppeteer Andrew Dunkman @adunkman

Slide 2

Slide 2 text

Outline 1 What are those? 2 Why Jest, and why Puppeteer? 3 The union of Jest and Puppeteer.

Slide 3

Slide 3 text

1 What are those?

Slide 4

Slide 4 text

1 What are those? End-to-End Testing Stack- Agnostic Jest and Puppeteer

Slide 5

Slide 5 text

1 What are those? agnostic: a person who claims neither faith nor disbelief in god Stack-Agnostic

Slide 6

Slide 6 text

1 What are those? Stack-Agnostic gnōstos gnōstikos gignōskein gnostic a- agnostic ENGLISH ENGLISH ENGLISH LATIN GREEK GREEK known know not’ late 16th century mid 19th century

Slide 7

Slide 7 text

1 What are those? stack-agnostic: denoting or relating to hardware or software that is compatible with many types of platforms or operating systems. (without knowledge of the platform) Stack-Agnostic

Slide 8

Slide 8 text

1 What are those? end-to-end test: tests the integration of system components, including the interaction between frontend and backend code. (from humans to electrons and back) End-to-End Testing

Slide 9

Slide 9 text

1 What are those? End-to-End Testing

Slide 10

Slide 10 text

1 What are those? “When engineers are provided with ready-to-use tools, they end up writing more tests, which in turn results in more stable and healthy code bases.” Jest and Puppeteer

Slide 11

Slide 11 text

1 What are those? Ready-to-use, familiar testing framework running in Node.js, supported by Facebook. Includes assertion tooling (expect) and mocking (faked functions) without configuration. Jest and Puppeteer

Slide 12

Slide 12 text

1 What are those? Thin Node.js wrapper around headless chromium, supported by Google. Chromium is the base of Chrome (and soon to be Edge). Uses DevTools protocol (no extra code executing in browser environment). Use JavaScript to control a real, invisible browser. Jest and Puppeteer

Slide 13

Slide 13 text

1 What are those?

Slide 14

Slide 14 text

Outline 1 What are those? 2 Why Jest, and why Puppeteer? 3 The union of Jest and Puppeteer.

Slide 15

Slide 15 text

2 Why Jest, and why Puppeteer?

Slide 16

Slide 16 text

2 Why Jest, and why Puppeteer? ✓ Automatic parallelization ✓ Console messages buffered ✓ Sandboxed with global state reset ✓ Supported by a major organization ✓ Lifecycle hooks

Slide 17

Slide 17 text

2 Why Jest, and why Puppeteer?

Slide 18

Slide 18 text

2 Why Jest, and why Puppeteer?

Slide 19

Slide 19 text

2 Why Jest, and why Puppeteer?

Slide 20

Slide 20 text

2 Why Jest, and why Puppeteer? test('using async/await for promises', async () => { const results = await thing.fetch(); expect(results).toEqual({}); });

Slide 21

Slide 21 text

2 Why Jest, and why Puppeteer? expect().toBe(1); expect().toEqual({}); expect().toBeNull(); expect().toBeUndefined(); expect().toBeTruthy(); expect().toBeGreaterThan(1); expect().toBeGreaterThanOrEqualTo(1); expect(0.2 + 0.2 + 0.2).toBeCloseTo(0.6, 2); expect('hello').toMatch(/hello/); expect([]).toContain('hello'); expect(fn).toHaveBeenCalled(); expect(fn).toHaveBeenCalledWith(args); expect(fn).toThrow(error);

Slide 22

Slide 22 text

2 Why Jest, and why Puppeteer? expect().toBe(1); expect().toEqual({}); expect().toBeNull(); expect().toBeUndefined(); expect().toBeTruthy(); expect().toBeGreaterThan(1); expect().toBeGreaterThanOrEqualTo(1); expect(0.2 + 0.2 + 0.2).toBeCloseTo(0.6, 2); expect('hello').toMatch(/hello/); expect([]).toContain('hello'); expect(fn).toHaveBeenCalled(); expect(fn).toHaveBeenCalledWith(args); expect(fn).toThrow(error);

Slide 23

Slide 23 text

2 Why Jest, and why Puppeteer? All expectations can be negated using expect().not expect(4).not.toBe(1); expect(1).not.toBeGreaterThan(6);

Slide 24

Slide 24 text

2 Why Jest, and why Puppeteer? expect() can be extended with custom assertions. expect.extend({ toBeBear(obj) { return { message: () => `expected ${obj} to have pr…`, pass: obj.bearable === 'yes', }; } });

Slide 25

Slide 25 text

2 Why Jest, and why Puppeteer? ✓ Built-in support for faking setTimeout() and setInterval(). ✓ Mock functions with jest.fn(() => {}). ✓ Mocked dependencies with jest.mock('module'). ✓ Repeated test setup and teardown with beforeEach() and afterEach(). ✓ Scoped tests with describe().

Slide 26

Slide 26 text

2 Why Jest, and why Puppeteer? ✓ It just works ✓ Up-to-date ✓ Real browser environment without hacks ✓ Supported by a major organization ✓ Fast enough to have a slow motion mode

Slide 27

Slide 27 text

const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://www.dunkman.me'); await page.screenshot({path: 'dunkman.me.png'}); await browser.close(); })(); 2 Why Jest, and why Puppeteer?

Slide 28

Slide 28 text

2 Why Jest, and why Puppeteer?

Slide 29

Slide 29 text

Inspecting page elements is similar to the DevTools console. page.$('#main-content'); page.$$('p'); page.evaluate(() => document.title); page.$eval('#search', el => el.value); page.$$eval('input', els => els.map(el => el.value)); 2 Why Jest, and why Puppeteer?

Slide 30

Slide 30 text

Accessing page properties can be done via page.$eval(). const dimensions = await page.$eval('html', el => { return { width: el.clientWidth, height: el.clientHeight, }; }); 2 Why Jest, and why Puppeteer?

Slide 31

Slide 31 text

Interacting with the page is simple. page.click('#my-button'); page.tap('#my-button'); page.hover('#my-button'); page.focus('#my-input'); page.type('#my-input', 'ICE was founded in 2003, abolish it'); page.select('select#colors', 'red'); 2 Why Jest, and why Puppeteer?

Slide 32

Slide 32 text

Waiting for the browser is straightforward with await and avoids race conditions. await Promise.all([ page.waitForNavigation(), page.click(link), ]); 2 Why Jest, and why Puppeteer?

Slide 33

Slide 33 text

2 Why Jest, and why Puppeteer?

Slide 34

Slide 34 text

Outline 1 What are those? 2 Why Jest, and why Puppeteer? 3 The union of Jest and Puppeteer.

Slide 35

Slide 35 text

3 The union of Jest and Puppeteer.

Slide 36

Slide 36 text

3 The union of Jest and Puppeteer. Install jest, puppeteer, and the lifecycle hooks to boot the browser when jest starts npm install --save-dev jest jest-puppeteer puppeteer

Slide 37

Slide 37 text

3 The union of Jest and Puppeteer. Configure Jest to load jest-puppeteer when starting by adding to package.json: { "name": "jest-puppeteer-example", "devDependencies": {...}, "jest": { "preset": "jest-puppeteer" } }

Slide 38

Slide 38 text

3 The union of Jest and Puppeteer. Writing your first test: // mytest.test.js test('i installed it correctly', async () => { await page.goto('https://www.example.com'); const heading = await page.$eval('h1', h1 => h1.innerText); expect(heading).toBe('Example Domain'); })

Slide 39

Slide 39 text

3 The union of Jest and Puppeteer. Let’s take a closer look.

Slide 40

Slide 40 text

3 The union of Jest and Puppeteer. Launch the browser visibly, increase the test runner timeout, or pause execution to aid in debugging. {headless: false, devtools: true} jest.setTimeout(100000); await jestPuppeteer.debug();

Slide 41

Slide 41 text

3 The union of Jest and Puppeteer.

Slide 42

Slide 42 text

Outline 1 What are those? 2 Why Jest, and why Puppeteer? 3 The union of Jest and Puppeteer.

Slide 43

Slide 43 text

Slides are available at dunkman.me/talks Have a thought or question? Come up front to chat in a small group.