# Handsontable visual testing
To avoid unintended changes to Handsontable's UI, we use visual regression testing.
## Overview
We run visual tests automatically by using the following tools:
| Tool | Description |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Playwright](https://playwright.dev/docs/intro) | An open-source testing framework backed by Microsoft. We use it to write and run visual tests. |
| [Argos](https://argos-ci.com/docs/visual-testing) | An external visual testing service. We use it to compare screenshots. |
| [GitHub Actions](https://github.com/handsontable/handsontable/actions) | GitHub's CI platform. We use it to automate our [test workflows](https://github.com/handsontable/handsontable/blob/develop/.github/workflows/test.yml). |
When you push changes to a GitHub pull request:
1. The [Visual tests linter](https://github.com/handsontable/handsontable/actions/workflows/visual-tests-linter.yml)
workflow checks the code of each visual test.
2. The [Tests](https://github.com/handsontable/handsontable/blob/develop/.github/workflows/test.yml) workflow runs all
of Handsontable's tests.
3. After all tests pass successfully, the [Visual tests](https://github.com/handsontable/handsontable/blob/develop/.github/workflows/test.yml#L432-L502)
job runs the visual tests and uploads the resulting screenshots to Argos.
4. Argos compares your feature branch screenshots against the reference branch (`develop`) screenshots
(so-called "reference", "baseline" or "golden" screenshots).
If Argos spots differences between two corresponding screenshots,
the **Visual tests** check on on your pull request fails, and you can't merge your changes to `develop`. In that case:
1. Open the log of the **Visual tests** job:
At the bottom of your pull request, find the **Visual tests** check. Select **Details**.
2. Open the Argos URL and [review the differences](https://argos-ci.com/docs/visual-testing#reviewing-visual-changes).
You can:
- [Reject the modified screenshots](https://argos-ci.com/docs/visual-testing#-reject-a-build-workflow), update your code,
and [re-run the visual tests](#run-visual-tests-through-github-actions).
- [Accept the modified screenshots](https://argos-ci.com/docs/visual-testing#-approving-a-build).
You can then merge your changes to `develop`.
As a result, the modified screenshots become the new baseline.
## Visual tests structure
Visual tests are divided into:
- multi-frameworks: tests run on Chromium using classic, horizon, horizon-dark, main and main-dark themes against Handsontable instance created in:
- Vanilla JS
- Angular
- React
- React (functional)
- Vue 2
- Vue 3
- cross-browser: tests run against vanilla JS Handsontable instance using:
- Chromium
- Firefox
- Webkit
There is a separate Playwright config for cross-browser tests: `playwright-cross-browser.config.ts`
## Visual tests demos
All the test examples are available at `examples/next/visual-tests` and configured to be served from `localhost:8082`
There main demo available for all frameworks is served on `/`. There are additional demos available only for vanilla JS (to be used with cross-browser tests):
- `/cell-types-demo`,
- `/arabic-rtl-demo`,
- `/custom-style-demo`,
- `/merged-cells-demo`,
- `/nested-headers-demo`,
- `/nested-rows-demo`,
## Run visual tests through GitHub Actions
Our GitHub Actions configuration runs the visual tests automatically, but you can run them manually as well:
1. On GitHub, at the bottom of your pull request, find the **Visual tests** check. Select **Details**.
2. On the left, next to the **Visual tests** job, select 🔄.
3. Select **Re-run jobs**.
## Run visual tests locally
You can manually run visual tests on your machine and then upload the resulting screenshots to Argos.
First, prepare your local visual testing environment:
1. Make sure you're using the Node and npm versions mentioned [here](https://handsontable.com/docs/react-data-grid/custom-builds/#build-requirements).
2. From the `./visual-tests/` directory, run `npm install`.
3. In the `./visual-tests/` directory, create a file called `.env`. In the file, add the Argos token:
```bash
ARGOS_TOKEN=xxx
```
Ask your supervisor about the token's value.
To run the visual tests locally:
1. From the `./visual-tests/` directory, run one of the following commands:
| Command | Action |
| ------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `npm run test` | Run multi-framework visual tests,
for all the configured frameworks,
using Chromium only. |
| `npm run test:cross-browser` | Run cross-browser visual tests,
using vanilla JS framework,
for all the supported browsers.
You can pass the test name to run a single cross-browser test: `npm run test:cross-browser borders`|
| `npx playwright test {{ file name }}` | Run a specific test.
For example: `npx playwright test mouse-wheel` |
The resulting screenshots are saved in `./visual-tests/screenshots/`.
2. From the `./visual-tests/` directory, run `npm run upload`.
3. Open the Argos URL displayed in the terminal.
## Write a new visual test
To add a new visual test:
1. On your machine, in the `./visual-tests/tests/` directory, create a new `.spec.ts` file.
Give your file a descriptive name. This name is later used in test logs and screenshot names.
- ✅ Good: `open-dropdown-menu.spec.ts`.
- ❌ Bad: `my-test-1.spec.ts`.
2. Copy the template code from `./visual-tests/tests/.empty-test-template.ts` into your file.
3. Write your test. For more information, see:
- [Playwright's docs](https://playwright.dev/docs/writing-tests)
- [Helpers](#helpers)
- [Take screenshots](#take-screenshots)
4. Push your changes to a pull request.
The [Visual tests linter](https://github.com/handsontable/handsontable/actions/workflows/visual-tests-linter.yml)
workflow checks the code of your test.
### Take screenshots
To capture a [screenshot](https://playwright.dev/docs/screenshots) and save it to a file,
add this line anywhere in your test:
```js
await page.screenshot({ path: helpers.screenshotPath() });
```
In each test, you can take as many screenshots as you want. For example:
```js
await cell.click();
await page.screenshot({ path: helpers.screenshotPath() });
await anotherCell.click();
await page.screenshot({ path: helpers.screenshotPath() });
```
To take a screenshot of a specific element of Handsontable,
use Playwright's [`locator()`](https://playwright.dev/docs/locators#locate-by-css-or-xpath) method. For example:
```js
const dropdownMenu = page.locator(helpers.selectors.dropdownMenu);
await dropdownMenu.screenshot({ path: helpers.screenshotPath() });
```
For cross-browser tests we are using
```js
await page.screenshot({ path: helpers.screenshotMultiUrlPath(testFileName, url, suffix) });
```
for easier screenshot identification.
### Helpers
To write tests faster, use the custom helper functions and variables stored in the `./visual-tests/src/helpers.ts` file.
#### `modifier`
Returns the current modifier key: `Ctrl` for Windows or `Meta` for Mac.
```js
// copy the contents of the selected cell
await page.keyboard.press(`${helpers.modifier}+c`);
```
#### `isMac`
Returns `true` if the test runs on Mac.
```js
if (helpers.isMac) {
// do something
}
```
#### `findCell()`
Returns the specified cell.
Syntax: `findCell({ row: number, cell: number, cellType: 'td / th' })`.
```js
const cell = helpers.tbody.locator(helpers.findCell({ row: 2, cell: 2, cellType: 'td' }));
await cell.click();
```
#### `findDropdownMenuExpander()`
Returns the button that expands the dropdown menu
(also known as [column menu](https://handsontable.com/docs/react-data-grid/column-menu/)) of the specified column.
Syntax: `findDropdownMenuExpander({ col: number })`.
```js
// select the column menu button of the second column
const changeTypeButton = table.locator(helpers.findDropdownMenuExpander({ col: 2 }));
await changeTypeButton.click();
```