SWC-6477: End-to-end testing for SWC
- SWC-6477Getting issue details... STATUS
Executive Summary
What technologies (Playwright, Cypress, Selenium, etc.) should we use to orchestrate tests?
Recommend using Playwright as end-to-end testing framework, because it
Utilizes Chrome DevTools Protocol (CDP) for direct control of the browser via websockets, which reduces test flakiness, increases test speed, and allows enhanced control of the browser with associated new features, as compared to tools implementing the WebDriver Protocol, which require an intermediary browser-driver.
Has cross-browser compatibility (Chrome, Firefox, Safari, Edge).
Has increasing popularity and community support and is backed by a large company (Microsoft).
Includes numerous feature differentiators that sets it apart from competitors, including out-of-the-box test parallelization locally and in CI, support for multiple users, multiple tabs, multiple browsers, and multiple origins, ability to write tests in multiple languages (TypeScript, Java, Python, .NET), superior developer tools, and locator and promise handling strategies that most closely align with existing WebEng Team products.
Associated trade-offs:
Not supported in much older browser versions and IE11.
Newer framework, with growing, but smaller community than other frameworks.
When should the end-to-end suite run? (e.g. per code change, cron schedule, before release)
Against what backend instance should the end-to-end suite run?
Recommend running end-to-end suite against backend dev stack locally during development and per code change in CI, so there is continual feedback about the impact of changes on functionality. Tests will need to clean-up any side-effects (e.g. creating a new project), so that tests can continue to pass against the same stack.
Consider creating a set of smoke tests that run against staging stack before release to exercise functionality that isn’t implemented on the dev stack (e.g. sending emails for account verification) and to verify key workflows against a copy of production data.
Test parallelization and sharding will hopefully allow the test suite to scale without increasingly slow run times, locally or in CI.
Can this work be generalized to also run against Portals?
If we wanted to use the same framework, certainly. If we wanted to re-use the same tests, potentially. SRC components are used in both SWC and Portals, so as long as flows are similar (or could be conditionally adjusted based on the current environment), the same tests could be run against both apps. There are configuration options (e.g. baseURL) that would support selecting the tested application at runtime, see here.
However, if the user flows are sufficiently different, then consider writing separate end-to-end tests or integration tests per application instead.
What scenarios should be tested?
Recommend testing the most critical features and important user workflows as well as features that are difficult to test in other formats, such as validating that data is persisted and displayed across multiple screens.
Recommend brainstorming as a team (and consider gathering input from other stakeholders, such as Governance, PMs, and Design) to create and prioritize a comprehensive list of critical features.
An initial list of important user workflows based on the Getting Started docs:
Log in (email, OAuth2 provider)
Create an account
Agree to code of conduct / terms of use
Request access to an entity
Create a project
Upload data, download data
Create a table, add data to a table, query a table
Other critical features to consider:
ACT / Governance, e.g. user cannot download file without meeting ACLs and ARs
Access workflows: e.g. a regular user requests access, an ACT user grants access, then the regular user has access
Action Items
Create ticket to introduce proof-of-concept to SWC – SWC-6514
Run against backend dev stack
Configure GitHub Actions with environment variables
Design test workflow so that can run tests with side effects in parallel locally (e.g. each dev has their own credentials) and potentially run CI (e.g. register new user for each run)
Publish doc to Confluence
Framework Selection
General Approach
I started by identifying a variety of browser automation tools (Cypress, Playwright, Puppeteer, Selenium WebDriver, TestCafe), and then narrowed down the list by evaluating their underlying protocols, cross-browser compatibility, relative popularity, and differentiating features. Once I had narrowed down to two frameworks, I set up a proof of concept with each to evaluate developer tools and to create a variety of small tests.
Background
End-to-end testing aims to simulate real user scenarios by testing the entire application flow. Browser automation tools are often used to automate the execution of these tests. Headless browsers, i.e. browsers that can run without displaying the UI, are useful for running these tests in CI. In the past, headless browsers weren’t native to the browsers that people actually used, but were developed separately (e.g. PhantomJS, based on WebKit). Browser automation tools used the WebDriver protocol, which uses a browser-driver to translate test script commands into commands the browser could execute.
However, since 2017, Chrome and Firefox have added native support for headless browsers, so automated tests can be run in the officially supported browsers. In addition, the Chrome DevTools Protocol was released, which allows direct communication between the test script and the browser via web sockets. New browser automation tools were released that took advantage of this protocol or the paradigm of direct communication with the browser.
A next generation W3C WebDriver protocol called WebDriver BiDi aims to standardize browser automation protocols and combine the best of both WebDriver “Classic” and CDP, but is currently under development.
I will start by evaluating the pros/cons of the protocols underlying the browser automation tools.
1) Underlying Protocols: Intermediary vs Direct Communication
| WebDriver Protocol | Chrome DevTools Protocol (CDP) | Native Scripting | WebDriver Bidi (BiDirectional) |
Description | Allows web browser automation using a browser-driver as an intermediary to translate the test script into commands that the browser can execute. Standardized by W3C. | Allows direct control of the browser via websockets. Maintained / standardized by the browser providers. | Runs the automation script “in-process” with the browser, so the browse is directly controlled by injected script | Next generation of W3C WebDriver protocol that aims to provide a stable API implemented by all browsers with bidirectional functionality via web sockets. In progress. |
Pros |
|
|
|
|
Cons |
|
|
|
|
Frameworks | Selenium WebDriver | Selenium WebDriver Devtools Service*, Playwright, Puppeteer, TestCafe | Cypress** | N/A |
*Selenium WebDriver offers a “Devtools Service”, which allows running CDP commands in tests. The service uses Puppeteer under the hood.
**Cypress currently uses its own implementation to control the browser in process, but per this comment, they are working on using CDP to communicate with the browser.
Summary
While the WebDriver protocol is a more established protocol with a large community, the lack of direct communication between the test script and browser execution leads to test flakiness. Additionally, there are certain features that can’t be readily implemented, such as manipulating network requests. While W3C is working to implement a next generation WebDriver protocol that uses web sockets for direct communication (WebDriver BiDi), the protocol is still in early stages and not fully implemented in any testing frameworks.
Both Chrome DevTools Protocol and Native Scripting approaches offer direct communication between the testing tool and browser, so the script can perform actionability checks before performing actions, such as waiting for network calls to finish or elements to appear before proceeding, which reduces test flakiness. New features, based on the enhanced control of the browser, can also be implemented.
Will proceed with tools that use protocols based on direct communication to decrease test flakiness and leverage features based on enhanced browser control: Cypress, Playwright, Puppeteer, Selenium WebDriver Devtools, TestCafe.
2) Cross-Browser Compatibility
| Cypress | Playwright | Puppeteer* | TestCafe |
Chrome | Yes | Yes | Yes | Yes |
Firefox | Yes | Yes | Experimental | Yes |
Safari | Experimental | Yes | No | Yes |
Edge | Yes | Yes | No | Yes |
*Selenium WebDriver Devtools Service uses Puppeteer under the hood.
Summary
Among the browser automation tools that directly communicate with the browser, either via CDP or via native scripting, Puppeteer has the least cross-browser compatibility without support for Safari or Edge.
Will proceed with tools that at least have experimental support for all major browsers: Cypress, Playwright, TestCafe.
3) Popularity / Community / Maintenance
Ideally, the selected testing framework will continue to be maintained and improved for many years into the future. Selecting an increasingly popular framework will also increase the likelihood that WebEng developers, both existing and new hires, are already familiar with the framework, which will decrease spin-up time and make it easier to maintain long term.
However, predicting which frameworks will make the cut is difficult. Here, popularity, a large community, and active maintenance of the framework are used as indirect measures of the framework’s potential longevity.
TestCafe is the oldest framework –
TestCafe: initially released Nov 7, 2016; 6.7 years old
Cypress: initially released Sep 10, 2017; 5.8 years old
Playwright: initially released Jan 31, 2020; 3.4 years old
However, Cypress has largest community, based on tags on stackoverflow (July 20, 2023):
[cypress]: 9,680 questions
[playwright]: 2,234 questions
[testcafe]: 1,845 questions
And the most downloads (July 20, 2023):
But Playwright popularity seems to be rapidly increasing, based on GitHub repository star history (July 20, 2023):
And more active management of their GitHub open issues (July 20, 2023):
Cypress: 1,336 open issues, 11,697 closed issues
Playwright: 616 open issues, 9,883 closed issues
TestCafe: 132 open issues, 4,409 closed issues
Summary
TestCafe is the oldest framework, but appears to be the least popular. Cypress is the most popular based on number of downloads and stackoverflow questions. However, Playwright appears to be increasing quickly in popularity since its release and has more active management of open issues.
Will proceed with the most popular or increasingly popular tools: Cypress, Playwright.
4) Cypress vs Playwright
Basics
| Cypress | Playwright |
Repo | ||
Company Backer | Cypress | Microsoft |
First release / Age | Sep 10, 2017, 5.8 years old
| Jan 31, 2020, 3.4 years old |
Size / Dependencies | 5MB, 42 dependencies (v12.17.2)
If we want to use Cypress Testing Library, then add: 42.8kB, 2 dependencies (v9.0.0) | 24.2kB, 1 dependency (v1.36.1) |
License | MIT license | Apache 2.0 |
Cost | Free, unless we want to use Cypress Cloud, which has pricing tiers (starting from free) - Scale Open-Source Testing with Cypress Cloud | Cypress Cloud Pricing | Free |
Supported Languages | Javascript only. Can support Typescript with configuration. Will not be able to support other languages due to its “in process” implementation.
https://docs.cypress.io/guides/references/trade-offs#Inside-the-browser
| Typescript, Python, Java, .NET
Supported languages | Playwright
|
Supported Operating Systems | Windows, MacOS, Linux
https://docs.cypress.io/guides/getting-started/installing-cypress#System-requirements | Windows, MacOS, Ubuntu
|
Cross-browser Compatibility | Chrome Firefox Safari (experimental) Edge | Chrome Firefox Safari Edge |
Used elsewhere in Web Engineering products | Yes, in Agora. However, implementation is currently minimal (one test) and doesn’t support the Cypress GUI. | No |
Features
| Cypress | Playwright |
Auto-wait and retry | Yes
| Yes
|
Component testing | Yes
Component Testing in Cypress | Cypress Documentation | Cypress Documentation | Experimental
|
Device size | Yes, can control viewport size, but generally requires plugins for more control over browser settings and permissions.
viewport | Cypress Documentation
| Yes, emulates viewport size, devices, browser settings (e.g. dark mode) and permissions, language/location/timezone out of the box.
|
Hover event support | No, but offers workarounds
| Yes
|
iFrame support | Limited, but planned to improve in the future.
| Yes
|
Mock network requests | Yes
| Yes
|
Multiple browser tabs | No, and cannot be supported in the future due to “in process” design.
https://docs.cypress.io/guides/references/trade-offs#Multiple-tabs | Yes
|
Multiple browsers | No, and cannot be supported in the future due to “in process” design.
https://docs.cypress.io/guides/references/trade-offs#Multiple-browsers-open-at-the-same-time | Yes
|
Multiple users | No, and cannot be supported in the future due to “in process” design.
https://docs.cypress.io/guides/references/trade-offs#Multiple-browsers-open-at-the-same-time | Yes
https://playwright.dev/docs/browser-contexts#multiple-contexts-in-a-single-test
|
Reuse authentication state | Yes
https://docs.cypress.io/api/cypress-api/custom-commands#Log-in-command-using-UI https://www.cypress.io/blog/2021/08/04/authenticate-faster-in-tests-cy-session-command/ | Yes
https://playwright.dev/docs/auth#basic-shared-account-in-all-tests |
Parallelization of tests (CI) | Yes, per file, but requires Cypress Cloud (which starts at free tier, but only for 3 users and limited test results)
https://www.cypress.io/pricing https://docs.cypress.io/guides/cloud/smart-orchestration/parallelization | Yes, out of the box, per file or can be configured per test in file
|
Parallelization of tests (locally) | Not supported / not recommended (would require too many resources), but could be done manually
https://docs.cypress.io/guides/cloud/smart-orchestration/parallelization https://stackoverflow.com/a/62501194 | Yes, out of the box, per file or can be configured per test in file
|
Test sharding | Yes, but (I think) that's their parallelization strategy -- split tests up and run on different machines, rather than running tests in parallel on the same machine
https://docs.cypress.io/guides/cloud/smart-orchestration/parallelization#Overview | Yes, with configuration options for GitHub Actions workflows
https://playwright.dev/docs/test-parallel#shard-tests-between-multiple-machines
|
Promise handling | “Promise-like” method chaining
https://mtlynch.io/notes/cypress-vs-playwright/#playwright-requires-less-domain-specific-knowledge https://www.qawolf.com/blog/why-qa-wolf-chose-playwright-over-cypress | Standard promises
https://mtlynch.io/notes/cypress-vs-playwright/#playwright-requires-less-domain-specific-knowledge https://www.qawolf.com/blog/why-qa-wolf-chose-playwright-over-cypress |
Tab key support | No
| Yes
https://playwright.dev/docs/api/class-keyboard#keyboard-down |
Test Isolation | Configurable
| Yes, via browser contexts
|
Developer Tools
| Cypress | Playwright |
IDE Integration |
|
|
Test generation |
|
|
Debugging |
|
|
Reviewing CI failures |
*Videos only available for Chromium-based browsers, i.e. Chrome/Edge https://docs.cypress.io/guides/guides/screenshots-and-videos https://www.cypress.io/blog/2023/01/31/introducing-in-app-test-results/ |
Explore recorded Playwright traces of tests, step forward/backward through each action and see what was happening
|
Reporting |
|
|
Nx Integration | Official @nx/cypress plugin
| Unofficial plugins only |
Visual testing | Not out of the box, but many plugins could be configured for this purpose
| Screenshot comparison with pixelmatch library, can build into test as a snapshot expectation
|
GitHub Actions | Docs include example workflows. Can set up to run in containers.
https://docs.cypress.io/guides/continuous-integration/github-actions#Cypress-GitHub-Action | Default workflow created when installing playwright. https://playwright.dev/docs/ci-intro
Could also be set up to run in containers and/or to shard tests, which could help reduce time to run tests as the test suite grows in size. |
Locator Strategies
The Web Engineering team currently uses the Testing Library in the synapse-web-monorepo and in MTB. Below is a table of the recommended locator strategies in Cypress vs Playwright.
| Testing-library - source | Cypress* - source | Playwright** - source |
Most recommended | getByRole | data-* attributes | getByRole (accessibility attributes) |
| getByLabelText | Text content | getByText (text content) |
getByPlaceholderText | name | getByLabel (form control by label text) | |
getByText | id | getByPlaceholder (input by placeholder) | |
getByDisplayValue | classes | getByAltText (element by text alternative) | |
getByAltText | roles without context | getByTitle (element by title attribute) | |
getByTitle |
| getByTestId (element based on data-testid) | |
Least recommended | getByTestId |
|
|
*Cypress Testing Library can be used with Cypress, so could use the same findByRole, findByLabelText, etc…Cypress docs state that their testing philosophy aligns closely with Testing Library’s ethos and approach to writing tests. So, could use Cypress without depending on test ids.
**Playwright doesn’t have ‘findBy…’ or ‘queryBy…’ queries – locators always auto-wait and retry when needed, so don’t have to worry about choosing the right method. List operations (e.g. ‘getAllBy…’) will also be handled by the `getBy` method – playwright will return a list if assertions imply a list is expected. https://playwright.dev/docs/testing-library#migrating-queries
Speed
According to this article, Cypress “exhibits a longer startup time” and “seems to be approximating Selenium speed in longer suites”, whereas for real-world scenarios, “Playwright tops the ranking” and shows “consistently faster execution times” in their tested scenarios.
Proof of Concept Evaluation
Set up each tool locally and run against the Synapse dev stack.
Use developer tools to generate a test and review test runs.
Write tests to
Show an alert when trying to create an account with an invalid email address
Login with username/password from environment variables
Re-use authenticated session across other tests to:
Navigate to “My Favorites” page
Show an alert when trying to create a new Project with an existing name
Cypress: Proof of Concept
https://github.com/Sage-Bionetworks/SynapseWebClient/compare/develop...hallieswan:SWC-6477-cypress
Setup
# install cypress - https://docs.cypress.io/guides/getting-started/installing-cypress#yarn-add
yarn add cypress --dev
# open cypress
yarn run cypress open
# for Cypress to find VS Code as IDE - https://github.com/cypress-io/cypress/issues/7456
# View -> Command Palette -> 'Install 'code' command in path' -> restart VS Code
# NOTE: cypress baseUrl for portal must be http://127.0.0.1:8888, not http://127.0.0.1:8888/Portal.html
# ...otherwise, cy.visit("/") will not work, since appending "/" after "Portal.html" is not valid
# install dotenv - https://www.npmjs.com/package/dotenv
# ...and then update .gitignore
yarn add dotenv --dev
# create .env with credentials
USERNAME="some dev stack user"
PASSWORD="the user's password"
# install cypress testing-library
yarn add --dev cypress @testing-library/cypress
# add this line to cypress/support/commands.js
# import '@testing-library/cypress/add-commands'
Running Tests
Cypress doesn’t offer a built-in way to run SWC for us before running Cypress tests. Instead, serve SWC in one terminal window, and then run Cypress in another terminal window. Each time the tests are run, SWC will quickly re-compile, since the majority of the tasks associated with mvn gwt:run
have already been completed.
NOTE: the tests will fail the first time they are run after starting up SWC, because SWC fully compiles after navigating to the Portal for the first time. However, each time that the tests are subsequently run, SWC compilation will be skipped because no input files changed. This could probably be adjusted so that SWC compiles before tests are run (e.g. see options in Cypress docs), but I didn’t dig into this further.
# terminal 1: serve portal
mvn gwt:run
# terminal 2:
# option1 -- run tests via GUI
yarn run cypress open
# --> Cypress GUI - point and click to run each test
# option2 -- run tests via CLI
yarn cypress run
Dev Experience
Installation / Set-up
Installation includes Cypress GUI. CI workflow must be installed separately. Cypress GUI walks through creating and running tests, but requires shifting to/from the IDE to edit tests (at least in VS Code).
Relatively straightforward to set up authentication to be re-used across tests, but setting environment variables was a bit of a hurdle since Cypress “environment variables” are not the same as OS-level environment variables, see here.
Test Generation
Offers Cypress Studio as an experimental feature within its GUI for generating tests, which worked moderately well. However, the locator strategy used in Cypress Studio followed the general Cypress strategy rather than the Testing Library strategy, i.e. test ids and class names over accessibility attributes or elements visible to the user. Generated tests would require more editing to match the locator strategy used elsewhere on the WebEng team.
Writing Tests
Cypress uses “promise-like” commands, but await does not work as expected, so nested `.then` blocks are needed to use dynamic values in tests, which is a slightly different strategy than what is used elsewhere on WebEng.
Selecting elements in Cypress was difficult at first, since the strategy is different from what is used elsewhere on WebEng (e.g. react-testing-library). However, Cypress Testing Library can be used with Cypress, which makes writing tests much more similar.
Playwright: Proof of Concept
Setup
# install playwright - https://playwright.dev/docs/intro
yarn create playwright
# create a directory to store authenticated browser state - https://playwright.dev/docs/auth#core-concepts
mkdir -p playwright/.auth
echo "\nplaywright/.auth" >> .gitignore
# install dotenv - https://www.npmjs.com/package/dotenv
yarn add dotenv --dev
# create .env with credentials and then update .gitignore
USERNAME="some dev stack user"
PASSWORD="the user's password"
Running Tests
During development, we can configure Playwright to run SWC for us before running any tests by setting webServer
in playwright.config.ts, as described here. However, this means that every time we re-run the tests, all tasks associated with mvn gwt:run
(or whatever we configure) will also be re-run.
For quick feedback when initially writing a test, we can comment out the webServer
block in the playwright.config.ts, serve SWC in one terminal window, and then easily re-run the tests in another terminal window. Each time the tests are run, SWC will quickly re-compile, since the majority of the tasks associated with mvn gwt:run
have already been completed. This greatly shortens the time to run tests. NOTE: this set-up works out of the box with SWC. Unlike the proof of concept Cypress set-up, the tests don't fail on the first run due to SWC compilation.
Configuration
Projects can be used to run sets of tests against different browsers or with different conditions. The filter testIgnore
can also be used to skip tests in particular projects, which can be useful for separating "logged out" vs "logged in" tests. For an example of this set up, see this guide.
Each project can also have different baseURLs, which could be used to run a subset of tests on Portals as well as SWC, while storing all of the tests in the same location. A single project can be run by using the --project
command line option. See more info and options in the Playwright docs.
Dev Experience
Installation / Set-up
Installation generates a GitHub Actions workflow, which can be adapted to our needs.
Straightforward to set up authentication and then re-use the authenticated user throughout, see here.
VS Code Extension and Integration
There are many features that integrate directly with VS Code, including identifying locators, debugging, and generating tests. See full list here.
Test Generation
Tests can also be generated via a GUI interface with Playwright Inspector, which can be launched with yarn playwright codegen
. Tests generally needed minimal tweaking for maintainability and alignment with general WebEng locator strategy.
Writing Tests
Playwright uses a similar locator strategy as the react-testing-library and standard promise handling, so writing tests felt familiar.
Summary
While Cypress has more extensive documentation and a larger community, the set up and dev experience using Playwright was superior. Its locator and promise handling strategies more closely aligned with existing Web Engineering products, which made for faster spin up and easier test writing. It was also much easier to get the tests running locally with SWC - Cypress doesn’t offer an option to automatically launch the app when running tests and failed on the first test run with SWC, since the portal hadn’t been compiled yet. Finally, the VS Code integration worked well to run tests from the IDE and the Playwright Inspector tools did a better job generating tests that aligned with our locator strategy out-of-the-box.
Additionally, Playwright’s feature differentiators are appealing for scalability (e.g. parallelization locally and in CI, configurable test sharding) and handling of more complex tests (e.g. handling hover events, iFrames, multiple tabs, browser contexts, users, etc).
Recommend Playwright.
Resources
Underlying protocols
Comparison of WebDriver Protocol and Chrome DevTools Protocol, with specific reference to Playwright features: https://medium.com/slalom-build/playwright-vs-webdriver-the-future-of-browser-automation-854a7ae63218
Comparison of architecture for common browser automation approaches: https://dev.to/jankaritech/different-approaches-protocols-to-automate-the-browser-39f1
WebDriver BiDi: https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/
Headless browser description: https://en.wikipedia.org/wiki/Headless_browser
Headless browser support in official browsers: https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/
WebDriver BiDi roadmap: https://developer.chrome.com/articles/webdriver-bidi/
Cypress vs Playwright
Playwright vs testing-library: https://playwright.dev/docs/testing-library#migrating-queries
Popularity and architecture: https://www.qawolf.com/blog/why-qa-wolf-chose-playwright-over-cypress
General features: https://cathalmacdonnacha.com/cypress-vs-playwright-which-is-best-for-e2e-testing
Comparison of writing tests for specific features: https://tsh.io/blog/cypress-vs-playwright/
Thorough comparison: https://mtlynch.io/notes/cypress-vs-playwright/
Appendix
Underlying Protocols: Architecture Diagrams
Architecture diagrams from: https://dev.to/jankaritech/different-approaches-protocols-to-automate-the-browser-39f1
WebDriver Protocol:
Chrome DevTools Protocol:
Native Scripting: