Skip to content

Commit

Permalink
feat: implement @sit-onyx/playwright-utils package (#2269)
Browse files Browse the repository at this point in the history
Relates to #1991

Implement `useMatrixScreenshotTest` and `adjustSizeToAbsolutePosition`
playwright utilities.
  • Loading branch information
larsrickert authored Dec 10, 2024
1 parent c88e678 commit ad309b3
Show file tree
Hide file tree
Showing 63 changed files with 1,615 additions and 956 deletions.
7 changes: 7 additions & 0 deletions .changeset/flat-tomatoes-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@sit-onyx/playwright-utils": minor
---

feat: implement `useMatrixScreenshotTest` and `adjustSizeToAbsolutePosition`

See our [docs](https://onyx.schwarz/development/packages/playwright-utils.html) for further information.
1 change: 1 addition & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"demo-app": "0.0.0",
"playground": "0.0.0",
"@sit-onyx/nuxt": "0.0.0",
"@sit-onyx/playwright-utils": "0.0.0",
"@sit-onyx/shared": "1.0.0"
},
"changesets": [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/docs/src/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export const CONFIG = {
{ text: "Headless composables", link: "/headless" },
{ text: "Icons", link: "/icons" },
{ text: "Nuxt", link: "/nuxt" },
{ text: "Playwright utilities", link: "/playwright-utils" },
{ text: "Storybook utilities", link: "/storybook-utils" },
{ text: "VitePress theme", link: "/vitepress-theme" },
].sort((a, b) => a.text.localeCompare(b.text)),
Expand Down
6 changes: 6 additions & 0 deletions apps/docs/src/development/packages/changelogs/[name].md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ Below you can find a full list of changes for the current and previous versions.

</div>

<div v-else-if="params.name === 'playwright-utils'">

<!--@include: @/../../../packages/playwright-utils/CHANGELOG.md-->

</div>

<div v-else>
<h1>Changelogs</h1>
<p>No changelog found for package "{{ params.name }}".</p>
Expand Down
281 changes: 281 additions & 0 deletions apps/docs/src/development/packages/playwright-utils.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
---
outline: [2, 3]
---

<script lang="ts" setup>
import packageJson from "../../../../../packages/playwright-utils/package.json";
</script>

# @sit-onyx/playwright-utils

<div class="hide-external-link">

[![npm version](https://badge.fury.io/js/@sit-onyx%playwright-utils.svg)](https://www.npmjs.com/package/@sit-onyx/playwright-utils)

</div>

{{ packageJson.description }}.

## Changelog

A full changelog can be found [here](/development/packages/changelogs/playwright-utils).

## Installation

Install the npm package with your corresponding package manager:

::: code-group

```sh [pnpm]
pnpm add -D @sit-onyx/playwright-utils@beta
```

```sh [npm]
npm install -D @sit-onyx/playwright-utils@beta
```

```sh [yarn]
yarn install -D @sit-onyx/playwright-utils@beta
```

:::

## Utilities

### useMatrixScreenshotTest

Creates a screenshot utility that can be used to capture matrix screenshots.
Useful for capturing a single screenshot/image that contains multiple variants of a component.

#### Example

![Example of a screenshot matrix for the OnyxButton](../../principles/contributing/example-matrix.png)

#### Usage

If not already installed, make sure to install the required dependencies:

::: code-group

```sh [pnpm]
pnpm add -D @playwright/test @playwright/experimental-ct-vue
```

```sh [npm]
npm install -D @playwright/test @playwright/experimental-ct-vue
```

```sh [yarn]
yarn install -D @playwright/test @playwright/experimental-ct-vue
```

:::

##### Step 1: Create matrix screenshot utility

First, we need to create the matrix screenshot test utility like shown below. There you can pass global options that are applied to all matrix screenshot tests.

::: code-group

```ts [playwright.ts]
import { useMatrixScreenshotTest } from "@sit-onyx/playwright-utils";

export const { executeMatrixScreenshotTest } = useMatrixScreenshotTest({
// optionally provide global/default options
});
```

:::

##### Step 2: Capture matrix screenshots

Afterwards, you can capture a single matrix screenshot like this:

::: code-group

```tsx [MyComponent.tsx]
import { executeMatrixScreenshotTest } from "./playwright";
import { test } from "@playwright/experimental-ct-vue";

test.describe("Screenshot tests", () => {
executeMatrixScreenshotTest({
name: "Button (default)",
columns: ["primary", "neutral", "danger"],
rows: ["default", "hover", "active", "focus-visible"],
component: (column) => {
return <OnyxButton label="Button" color={column} />;
},
hooks: {
beforeEach: async (component, page, column, row) => {
if (row === "hover") await component.hover();
if (row === "focus-visible") await page.keyboard.press("Tab");
if (row === "active") await page.mouse.down();
},
},
});
});
```

:::

<br>

#### Perform accessibility tests <Badge text="optional" type="warning" />

The matrix screenshot utility integrates nicely with features like [accessibility testing](https://playwright.dev/docs/accessibility-testing).

To perform accessibility tests for all individual screenshots (column/row combinations), set it up like described below.

##### Step 1: Install axe-core

::: code-group

```sh [pnpm]
pnpm add -D @axe-core/playwright
```

```sh [npm]
npm install -D @axe-core/playwright
```

```sh [yarn]
yarn install -D @axe-core/playwright
```

:::

##### Step 2: Setup global hook

We can now set up a global hook when calling `useMatrixScreenshotTest()` (see [Create matrix screenshot utility](#step-1-create-matrix-screenshot-utility)) that will run the accessibility test after every screenshot.

Therefore, update your already existing setup like so:

::: code-group

```ts [playwright.ts]
import { useMatrixScreenshotTest } from "@sit-onyx/playwright-utils";

/**
* Creates an `AxeBuilder` with common configuration that should be used for accessibility tests.
*
* @see https://playwright.dev/docs/accessibility-testing#creating-a-fixture
*/
export const createAxeBuilder = (page: Page) => {
return new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"]);
};

export const { executeMatrixScreenshotTest } = useMatrixScreenshotTest<MatrixScreenshotHookContext>(
{
defaults: {
hooks: {
afterEach: async (component, page, column, row) => {
// ARRANGE (execute accessibility tests)
const axeBuilder = createAxeBuilder(page);
const accessibilityScanResults = await axeBuilder.analyze();

// ASSERT
expect(
accessibilityScanResults.violations,
`should pass accessibility checks for ${column} ${row}`,
).toEqual([]);
},
},
},
},
);
```

:::

That's it. The accessibility tests will now be performed for every single screenshot.

##### Step 3: Disable rules for individual tests <Badge text="optional" type="warning" />

Sometimes, it might be necessary to disable certain accessibility rules for an individual test.
We can easily support this by making use of the `context` argument that is passed to the `afterEach` hook.

Adjust your global configuration like this to support disabling accessibility rules.

::: code-group

```ts [playwright.ts]
import { useMatrixScreenshotTest } from "@sit-onyx/playwright-utils";

export type MatrixScreenshotHookContext = {
/**
* Rules to disable when performing the accessibility tests.
* **IMPORTANT**: Should be avoided! If used, please include a comment why it is needed.
*
* @see https://playwright.dev/docs/accessibility-testing#disabling-individual-scan-rules
*/
disabledAccessibilityRules?: string[];
};

export const { executeMatrixScreenshotTest } = useMatrixScreenshotTest<MatrixScreenshotHookContext>(
{
defaults: {
hooks: {
afterEach: async (component, page, column, row, context) => {
// ARRANGE (execute accessibility tests)
const axeBuilder = createAxeBuilder(page);

if (context?.disabledAccessibilityRules?.length) {
axeBuilder.disableRules(
DEFAULT_DISABLED_AXE_RULES.concat(context.disabledAccessibilityRules),
);
}

const accessibilityScanResults = await axeBuilder.analyze();
// ...
},
},
},
},
);
```

:::

For each individual test, you can now optionally disable rules by defining them in the `context`:

::: code-group

```tsx [MyComponent.tsx]
import { executeMatrixScreenshotTest } from "./playwright";
import { test } from "@playwright/experimental-ct-vue";

test.describe("Screenshot tests", () => {
executeMatrixScreenshotTest({
name: "Button (default)",
context: {
disabledAccessibilityRules: ["color-contrast"],
},
// ...
});
});
```

:::

### adjustSizeToAbsolutePosition

Sets the component size to fit all absolute positioned content so it is fully included in screenshots.
Useful if component includes flyouts etc. that use CSS `position: absolute`.

```tsx
import { adjustSizeToAbsolutePosition } from "@sit-onyx/playwright-utils";
import { test, expect } from "@playwright/experimental-ct-vue";
import MyComponent from "./MyComponent.vue";

test("my example test", async ({ mount }) => {
const component = await mount(<MyComponent />);

// open/show absolute positioned content, e.g. a tooltip
await component.getByRole("tooltip").click();

// adjust component size to include tooltip so its shown/included in the screenshot
await adjustSizeToAbsolutePosition(component);

await expect(component).toHaveScreenshot("screenshot.png");
});
```
Binary file modified apps/docs/src/principles/contributing/example-matrix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 10 additions & 8 deletions apps/docs/src/principles/contributing/testing-example.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ test.describe("Screenshot tests", () => {
name: "OnyxComponent (densities)",
rows: ["default", "hover", "active", "focus-visible", "skeleton"],
columns: DENSITIES,
beforeScreenshot: async (component, page, column, row) => {
/**
* TODO: Prepare the component before the screenshot
* e.g.:
*/
if (row === "hover") await component.hover();
if (row === "focus-visible") await page.keyboard.press("Tab");
if (row === "active") await page.mouse.down();
hooks: {
beforeEach: async (component, page, column, row) => {
/**
* TODO: Prepare the component before the screenshot
* e.g.:
*/
if (row === "hover") await component.hover();
if (row === "focus-visible") await page.keyboard.press("Tab");
if (row === "active") await page.mouse.down();
},
},
component: (column, row) => (
// TODO: Set the props based on the given row and column
Expand Down
19 changes: 19 additions & 0 deletions packages/playwright-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div align="center" style="text-align: center">
<picture>
<source media="(prefers-color-scheme: dark)" type="image/svg+xml" srcset="https://raw.githubusercontent.com/SchwarzIT/onyx/main/.github/onyx-logo-light.svg">
<source media="(prefers-color-scheme: light)" type="image/svg+xml" srcset="https://raw.githubusercontent.com/SchwarzIT/onyx/main/.github/onyx-logo-dark.svg">
<img alt="onyx logo" src="https://raw.githubusercontent.com/SchwarzIT/onyx/main/.github/onyx-logo-dark.svg" width="160px">
</picture>
</div>

<br>

# onyx Playwright utils

Utilities for Vue component testing with Playwright created by [Schwarz IT](https://it.schwarz).

<br />

## Documentation

You can find our documentation [here](https://onyx.schwarz/development/packages/playwright-utils.html).
29 changes: 29 additions & 0 deletions packages/playwright-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@sit-onyx/playwright-utils",
"description": "Utilities for Vue component testing with Playwright",
"version": "0.0.0",
"type": "module",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/schwarzit/onyx",
"directory": "packages/playwright-utils"
},
"bugs": {
"url": "https://github.com/schwarzit/onyx/issues"
},
"files": [
"src"
],
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "vue-tsc --noEmit"
},
"peerDependencies": {
"@playwright/experimental-ct-vue": ">= 1",
"@playwright/test": ">= 1",
"vue": ">= 3"
}
}
2 changes: 2 additions & 0 deletions packages/playwright-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./screenshots/index";
export * from "./screenshots/types";
Loading

0 comments on commit ad309b3

Please sign in to comment.