At the start of this year, we could only write end-to-end tests using Selenium in Scala here at Lucid. This was just fine for the developers here who mostly write in Scala. The problem was that learning Scala and Selenium was a high bar of entry for developers to just write an end-to end-test. We have many devs who almost exclusively write in TypeScript. As newcomers to Scala, making an end-to-end test for new features was so difficult that often the tests just wouldnât get written.
When I found out about Puppeteer, it seemed like the right tool to solve this problem. Developers could write tests in TypeScript, a language they are more familiar with. We already used Jasmine for writing unit tests, so the ability to create Puppeteer tests with Jasmine was an obvious win. Devs can also connect Chrome DevTools when running tests, which allows them to use a debugger they are familiar with. All of these features looked ideal for lowering the bar of entry to writing end-to-end tests. Puppeteer also came with a few advantages over Selenium.
Simpler JavaScript execution
A powerful feature of both Selenium and Puppeteer is the ability to run JavaScript in the browser. The uses of this feature are nearly endless, and using this feature in Puppeteer is nearly effortless. Compare these two snippets of code: Scala + Seleniumval evalResult = Json.parse(driver.executeAsyncScript(âââ
var callback = arguments[arguments.length - 1];
asyncFunction().then(callback);
âââ).asInstanceOf[String])
Â
TypeScript + Puppeteerconst evalResult = await page.evaluate(() => asyncFunction());
Right away the TypeScript version is simpler and comes with some additional advantages. First, the TypeScript version automatically handles exceptions. If asyncFunction fails in the Selenium version, you would not get an error; instead it would time out. You could, and probably should, make a wrapper function that simplifies calling JavaScript and correctly handles errors if you go with Selenium. However, since the base implementation is already simpler, Puppeteer is a better choice here. No need to modify the interface.
The Puppeteer version also has the advantage of being type checked by TypeScript. You can declare functions and variables used inside of evaluate, and if you have syntax or type errors, TypeScript will catch those errors. In Selenium, you wonât catch the errors until you attempt to run the test.
The core of these advantages comes down to having the test driver use the same language the browser does. This makes connecting the two much more seamless. As a note, you can write Selenium tests in TypeScript and implement a similar seamless implementation of evaluate, but that wasnât an option for our Scala codeâ that is why I am listing this as a reason I wanted to switch to Puppeteer.
