Testing ES6 Module in Cypress Electron Browser

Adding support to script type=module while testing Hyperapp as ES6 module.

If you have a tiny framework, importing it as ES6 module is super convenient for quick demos. For example Hyperapp can be imported and used right away in most browsers. Original tweet.

Hyperapp works inside script type="module"

This is awesome - importing ES6 modules works with some caveats. For other browsers we do want to build ES5 bundle as an alternative to <script type="module"> syntax. But what about testing? Can we test the above Hyperapp without transpiling it?

If we take my favorite test runner Cypress - and it is my favorite in part because I am building it, and in part because it is awesome - yes, you can. Cypress can control multiple browsers. Chrome already supports <script type="module"> syntax. The built-in Electron browsers does not, and that's a shame - Cypress records video of the Electron browser on CI which is a great advantage when a test fails. In this blog post I will show how to add es6 module support to Electron browser.

note: you can find the source code in the repository bahmutov/hyperapp-es6-module-cypress

I copied the example code from the tweet into index.html, and have written a simple test for the counter program.

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
10
11
beforeEach(() => {
cy.visit('index.html')
})
it('loads', () => {
cy.contains('h1', '0')
cy
.contains('+')
.click()
.click()
cy.contains('h1', '2')
})

The test passes when Cypress uses Chrome browser, but fails in Electron - because no code is executing. Electron simply ignores the type="module" script it does not know how to handle.

Electron skips ES6 module script

We need to somehow transpile ES6 module code (and any other script it might load using import statement) into "normal" bundle. In Node I would use a bundler like rollup, but what can I use in the browser? I found browser-es-module-loader - while it warns that it is not suitable for production, this code is good enough to be used during testing. I cloned and built the repository. It produced two scripts that add ES6 module support to any browser; I placed these two scripts into the dist folder in my test repo. We need to load these scripts before loading any other code on the index.html page. Luckily this is easy to do - by using onBeforeLoad option in cy.visit method.

spec.js
1
2
3
4
5
6
7
8
9
10
11
12
const addScript = (document, src) => {
// force immediate load
document.write(`<script src="${src}"></script>`)
}
beforeEach(() => {
cy.visit('index.html', {
onBeforeLoad: win => {
addScript(win.document, 'dist/babel-browser-build.js') // must be first
addScript(win.document, 'dist/browser-es-module-loader.js')
},
})
})

After the update, Electron browser suddenly "speaks" ES6 modules, and our Hyperapp application is executing, and the test passes.

App is running in Electron

Related info

While enabling ES6 module support in Electron is nice, for wider code support, bundling code is still the way to go. In that case, end-to-end tests should run against the bundled code. For other tests, like unit tests, please consider "framework-to-Cypress" unit test bridges that allow loading individual components and testing them in the full browser environment.