Vaadin UI Testing with Playwright
In my Vaadin projects, I usually use Karibu Testing for unit and/or Vaadins TestBench for end-to-end tests.
Karibu Testing is an open source testing framework developed by Martin Vysny. Karibu Testing mocks the Vaadin Servlet environment and is the perfect choice when you want to do test-driven development. It’s very fast because no servlet container and no browser are involved.
TestBench is the commercial UI testing framework based on Selenium. It extends Selenium with Webcomponents and Vaadin-specific functionality. Testing with TestBench is often slow and flaky. I encountered non-reproducible problems when running the test. So I was looking for an alternative. This article explores Playwright, a powerful alternative promising faster execution and broader browser support.
Introducing Playwright
Two years ago, Microsoft launched Playwright. Playwright is available for Node.js, Python, Java, and C# and promises:
– Any browser, any platform, one API
– Resilient, no flaky tests
– No trade-offs, no limits
– Full isolation, fast execution
– Powerful tooling
“No flaky tests” and “fast execution” – sounds exactly what I was looking for!
Getting Started with Playwright
Add Playwright Dependency
To start with Playwright, you have to add a dependency. In my case, I’m using Maven:
<dependency> <groupId>com.microsoft.playwright</groupId> <artifactId>playwright</artifactId> <version>1.23.0</version> <scope>test</scope> </dependency>
Create a Base Test Class
To simplify the writing of the tests, I recommend creating a base class. Before all tests, we create an instance of Playwright and launch the browser. And before each test, a new BrowserContext and a new page are created to isolate the test methods.
public class PlaywrightIT { private static Playwright playwright; private static Browser browser; protected Page page; private BrowserContext browserContext; @BeforeAll static void beforeAll() { playwright = Playwright.create(); BrowserType browserType = playwright.chromium(); BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions(); launchOptions.headless = false; browser = browserType.launch(launchOptions); } @AfterAll static void afterAll() { browser.close(); playwright.close(); } @BeforeEach void beforeEach() { browserContext = browser.newContext(); page = browserContext.newPage(); } @AfterEach void afterEach() { page.close(); browserContext.close(); } }
Writing Playwright Tests for Vaadin Applications
The application we want to test is an open-source application I’ve created to manage track and field events.
The dashboard shows series and competitions with the rankings.
Navigating and Interacting with Elements
In the first test, we want to navigate to the dashboard. The test must check if the title equals Dashboard and count how many series are displayed. With the method locator of the page instance, we can use CSS selectors to select the elements on the page.
public class DashboardViewIT extends PlaywrightIT { @Test void series_are_displayed() { page.navigate("http://localhost:8484/"); Locator viewTitle = page.locator("#view-title"); assertThat(viewTitle.innerText()).isEqualTo("Dashboard"); Locator seriesLayouts = page.locator(".series-layout"); assertThat(seriesLayouts.count()).isEqualTo(6); } }
Easy, isn’t it? So let’s look at a more difficult test.
Handling Login and Protected Pages
The application also has a password-protected part for managing the data of the track and field events. The login screen is displayed when the user tries to access this part.
As with other UI testing frameworks, it’s recommended to create so-called page objects to encapsulate parts of the UI. The LoginPO has a single method that fills in the username and password and clicks on the sign-in button.
public class LoginPO { private final Page page; public LoginPO(Page page) { this.page = page; } public void login(String username, String password) { page.fill("vaadin-text-field[name='username'] > input", username); page.fill("vaadin-password-field[name='password'] > input", password); page.click("vaadin-button"); } }
We use the page object in the next test. In the “before each” method, we navigate to the organization’s page and sign in using the LoginPO. Then we check the title to see if the navigation after the login was successful.
@BeforeEach void login() { page.navigate("http://localhost:8484/organizations"); new LoginPO(page).login(System.getenv("JTAF4_TEST_USERNAME"), System.getenv("JTAF4_TEST_PASSWORD")); Locator locator = page.locator("#view-title"); assertThat(locator.innerText()).isEqualTo("Organisationen"); }
After successful login, the protected page is displayed.
Custom Elements for Vaadin Grids
In the test method, we first get the grid and then check if the expected data is contained in the grid.
GridElement organizationsGrid = new GridElement( page.locator("id=organizations-grid").elementHandle()); organizationsGrid.waitForCellByTextContent("CIS"); organizationsGrid.waitForCellByTextContent("Concours InterSection"); organizationsGrid.waitForCellByTextContent("TVE"); organizationsGrid.waitForCellByTextContent("Turnverein Erlach");
The used GridElement is a custom class to make using the Vaadin Grid webcomponent simpler in tests. For example, the scrollToIndex method executes JavaScript on the Grid object in the DOM.
public class GridElement { private final ElementHandle elementHandle; public GridElement(ElementHandle elementHandle) { this.elementHandle = elementHandle; } public void scrollToIndex(int index) { elementHandle.evaluate("(grid, index) => grid.scrollToIndex(index)", index); } public ElementHandle waitForCellByTextContent(String textContent) { return elementHandle.waitForSelector(String.format("vaadin-grid-cell-content >> '%s'", textContent)); } public ElementHandle getCellByTextContent(String textContent) { return elementHandle.querySelector(String.format("vaadin-grid-cell-content >> '%s'", textContent)); } }
After that, we click the Add button and fill some test data into the input fields, and hit Save:
page.locator("id=add-button").click(); page.locator("id=vaadin-text-field-0").fill("TST"); page.locator("id=vaadin-text-field-1").fill("Test"); page.locator("id=edit-save").click();
Now we can check if the grid contains the added organization, and finally, we click on the delete button to remove the test data.
organizationsGrid.waitForCellByTextContent("TST"); organizationsGrid.waitForCellByTextContent("Test"); page.locator("id=delete-organization-TST").click(); page.locator("id=delete-organization-confirm-dialog-confirm").click();
Conclusion
Compared to TestBench, Playwright is running much faster. On the other side, TestBench is made for Vaadin therefore selecting and interacting with Vaadin components is easier.
I will continue to write tests with Playwright to gain more experience with it.
Stay tuned for more blogs about Playwright and Vaadin.