Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mock Native API in your E2E test

joe_re
November 06, 2017

Mock Native API in your E2E test

ElectronMeetup in Tokyo LT (https://connpass.com/event/69473/)

How to run spectron E2E test in case of using Native API in your specs.

joe_re

November 06, 2017
Tweet

More Decks by joe_re

Other Decks in Technology

Transcript

  1. Mock Native API
    in your E2E test
    2017/11/06 ElectronMeetup in Tokyo LT
    @joe_re

    View Slide

  2. Who am I?
    twitter: @joe_re
    github: @joe­re
    community: Nishinippori.rb, GraphQL Tokyo Organizer

    View Slide

  3. Book

    View Slide

  4. Applications
    CafePitch
    Markdown Driven PresentationTool
    Tubutler
    Simple and usefull Youtube player

    View Slide

  5. Today, I talk about spectron E2E test
    in case of using 'Native API'
    in your spec.

    View Slide

  6. Spectron

    View Slide

  7. What is Spectron?
    E2E Testing tool for Electron appplication.
    You can call ElectronAPI in your spec via ChromeDriver and
    WebDriver.io.

    View Slide

  8. Example
    const app = new spectron.Application({ path: 'path/to/your/app' });
    describe('application launch', function () {
    this.timeout(10000);
    beforeEach(function () {
    return app.start();
    });
    afterEach(function () {
    return app.stop();
    });
    it('shows an initial window', function () {
    return app.client.getWindowCount().then(function (count) {
    assert.equal(count, 1);
    });
    });
    });

    View Slide

  9. Strong Points
    You can access any DOM via Webdriver.io.
    You can call any Electron API in your spec.
    No need setup script.(use Chromium inside Electron)
    Support CI services.(Travis, AppVeyor, etc)

    View Slide

  10. DEMO

    View Slide

  11. Spectron is nice.
    But it can't access Native API.
    (latest v3.7.2)
    So it can't mock Menu, Dialog, etc
    modules...
    https://github.com/electron/spectron/issues/94

    View Slide

  12. This is simple solution, but ugly...
    if (process.env.NODE_ENV === "production") {
    // production code here
    } else {
    // test code here
    }

    View Slide

  13. I want to write a spec which
    doesn't affect production code.

    View Slide

  14. I created two modules to resolve it.
    spectron­fake­menu
    https://github.com/joe­re/spectron­fake­menu
    spectron­fake­dialog
    https://github.com/joe­re/spectron­fake­dialog

    View Slide

  15. Usage
    spectron­fake­menu
    const Application = require('spectron').Application;
    const fakeMenu = require('spectron-fake-menu');
    const app = new Application({ path: electron, args: [ path.join(__dirname, '.') ] });
    fakeMenu.apply(app); // apply fake menu
    await app.start();
    fakeMenu.clickMenu('Config'); // 'Config' Menu click
    fakeMenu.clickMenu('File', 'CloseTab'); // File->CloseTab Menu click
    spectron­fake­dialog
    const Application = require('spectron').Application;
    const fakeDialog = require('spectron-fake-dialog');
    const app = new Application({ path: electron, args: [ path.join(__dirname, '.') ] });
    fakeDialog.apply(app);
    await app.start();
    fakeDialog.mock([ { method: 'showOpenDialog', value: ['faked.txt'] } ]));
    // write your specs

    View Slide

  16. How do they work?
    Electron provides '­­require' option same as Node.js.
    It can preload a module before run main script.
    These modules inject extra IPC and provide API which call
    or mock Native API inside your specs.

    View Slide

  17. Rough image

    View Slide

  18. Implementation(spectron­fake­dialog)
    const path = require('path');
    let _app = null;
    function apply(app) {
    _app = app;
    _app.args.unshift(path.join(__dirname, 'preload.js'));
    _app.args.unshift('--require');
    return _app;
    }
    function mock(options) {
    return _app.electron.ipcRenderer.sendSync('SPECTRON_FAKE_DIALOG/SEND', options);
    }
    module.exports = { apply, mock };

    View Slide

  19. Implementation(spectron­fake­dialog)
    preload script
    const { dialog, ipcMain, BrowserWindow } = require('electron');
    //... some logics for creating mocked function ...
    function fake(options) {
    options.forEach(v => {
    if (dialog[v.method]) {
    dialog[v.method] = mockFunction.bind(null, v.value);
    } else {
    throw new Error(`can't find ${v.method} on dialog module.`);
    }
    });
    }
    ipcMain.on('SPECTRON_FAKE_DIALOG/SEND', (e, options) => {
    fake(options);
    e.returnValue = true;
    });

    View Slide

  20. Please use they if you'd like.

    View Slide

  21. Thank you for your attention!

    View Slide