How OctoLinker Runs Automate E2e Tests Using Puppeteer

Update: Due to recent changes in GitHub, OctoLinker is no longer functional. I managed to keep it running for 9 years despite various platform updates, but this latest change has made it impossible to maintain its functionality. Read the full announcement for more details.

OctoLinker is a browser extension for GitHub.com that turns import statements into hyperlinks. This allows you to navigate through projects much faster. Since this week, we completed our testing pyramid by adding E2E testing which is really exciting. E2E stand for End to End testing and the following lines from Protractor explain it the best:

As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to verify the correctness of new features, catch bugs and notice regressions. Unit tests are the first line of defense for catching bugs, but sometimes issues come up with integration between components which can't be captured in a unit test. End-to-end tests are made to find these problems.

We're using Puppeteer which allows us to spin up a Chrome instance which then loads the OctoLinker extension. Once Chrome has started we can navigate to any website which is github.com in our case.

In our repository there is a folder called fixtures which contains all our dummy files which we use for our E2E testing. If you open one of those files, you will see a special annotation which we use to specify that the next line should resolve to the given target:

// When the target is an external url
// @OctoLinkerResolve(https://nodejs.org/api/fs.html)
require('fs')
// When the target is a relative file
// @OctoLinkerResolve(<root>/path/to/foo.js)
require('./foo.js')

The annotation is language-agnostic so you can also use it for Ruby or any other language:

# @OctoLinkerResolve(https://rubygems.org/gems/rake)
require 'rake'

It even works within JSON files:

{
  "dependencies": {
    "//": "@OctoLinkerResolve(https://github.com/lodash/lodash)",
    "lodash": "^1.2.3"
  }
}

Before we invoke jest, we scan the fixtures folder for the annotations described above and write a file named fixtures.json to the disk.

[
 {
  "url": "https://github.com/OctoLinker/OctoLinker/blob/master/e2e/fixtures/javascript/nodejs/gentle-resonance-3436.js",
  "content": "require('./proud-tooth-7361');",
  "targetUrl": "/javascript/nodejs/proud-tooth-7361.js",
  "lineNumber": 2
 },
 {
  "url": "https://github.com/OctoLinker/OctoLinker/blob/master/e2e/fixtures/javascript/npm/package.json",
  "content": "\"underscore\": \"1.2.3\"",
  "targetUrl": "https://github.com/jashkenas/underscore",
  "lineNumber": 8
 },
 ...
]

This JSON file then gets loaded by our actual test file automated.test.js to scaffold those tests on-the-fly. One by one we go through all items in this array and navigate to the specified url. A querySelector clicks our OctoLinker link on the given line number and finally we compare if the served urls is qual to targetUrl. Super simple, right?

Conclusion

Puppeteer makes E2E super easy, especially with the help of jest-puppeteer. We put a lot of effort to make it easy to contribute new E2E tests. It's literally nothing more than creating a file within e2e/fixtures and make sure the annotations are correct. If you have a minute or two, we would really appreciate a pull request from you!