Skip to content

Commit 0cfa555

Browse files
fixup! Add writing-tests-guidelines.md (#612)
1 parent 9a62aa4 commit 0cfa555

File tree

2 files changed

+152
-60
lines changed

2 files changed

+152
-60
lines changed

mkdocs.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@ nav:
142142
- Translations: 'docs/customize/translations.md'
143143
- Contribute:
144144
- General Guidelines: 'docs/contribute/general-guidelines.md'
145-
- Testing Guidelines: 'docs/contribute/testing-guidelines.md'
145+
- Testing Guidelines:
146+
- Testing: 'docs/contribute/testing-guidelines.md'
147+
- Writing Tests: 'docs/contribute/writing-tests-guidelines.md'
146148
- API Guidelines: 'docs/contribute/api.md'
147149
- Composition: 'docs/contribute/composition.md'
148150
- CSS Guidelines: 'docs/contribute/css.md'
Lines changed: 149 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Playwright Testing Structure
1+
## Playwright Testing Structure
22

33
This document outlines the structure and guidelines for writing Playwright
44
tests, ensuring consistency and maintainability throughout the codebase.
@@ -7,8 +7,10 @@ tests, ensuring consistency and maintainability throughout the codebase.
77

88
Playwright tests are organized into multiple files and folders, each
99
serving a specific purpose.
10+
1011
The complete component structure should be as follows:
1112

13+
```text
1214
<ComponentName>/
1315
├── __tests__/
1416
│ ├── <ComponentName>.spec.tsx-snapshots/
@@ -17,6 +19,7 @@ The complete component structure should be as follows:
1719
│ ├── <ComponentName>.spec.tsx
1820
│ ├── <ComponentName>.story.tsx
1921
├── ...rest component files
22+
```
2023

2124
- `<ComponentName>` - Root folder of the component, named after
2225
the component itself.
@@ -29,12 +32,13 @@ described below.
2932
in tests. These components should be functional without requiring any
3033
properties to be passed from the tests.
3134

32-
## <ComponentName>.spec.tsx File Structure
35+
## File Structure of `<ComponentName>.spec.tsx`
3336

3437
Playwright tests follow a structured format to ensure readability
3538
and scalability. Each displayed level represents a `test.describe(...)` block.
3639
The structure consists of:
3740

41+
```text
3842
<ComponentName>/
3943
├── base/
4044
│ ├── visual/
@@ -45,6 +49,7 @@ The structure consists of:
4549
│ ├── visual/
4650
│ ├── non-visual/
4751
│ ├── functionality/
52+
```
4853

4954
- `<ComponentName>` - Groups all tests for the tested component.
5055
- `base` - Contains component tests without any additional layout.
@@ -60,11 +65,11 @@ Test block categories can be expanded or removed depending on the nature
6065
of the tested component and whether a predefined test block is applicable
6166
in a specific case.
6267

63-
## <ComponentName>.story.tsx File Structure
68+
## File Structure of `<ComponentName>.story.tsx`
6469

6570
The `<ComponentName>.story.tsx` file should include all component variants
6671
tested in `<ComponentName>.spec.tsx`. Components should be organized
67-
in the following order:
72+
in the following order:
6873

6974
1. Component for normal tests (`<ComponentName>ForTest`)
7075
2. Component for `ref` attribute tests (`<ComponentName>ForRefTest`)
@@ -75,14 +80,14 @@ in the following order:
7580

7681
Each test case should follow the properties defined in the `PropTest` type.
7782
This type includes the properties `name`, `onBeforeTest`, `onBeforeSnapshot`,
78-
and `props`, which define the component setup for the actual test case.
83+
and `props`, which define the component setup for the actual test case.
7984

8085
- `name` - The name of the test case, following the naming conventions
81-
described in the next chapter.
86+
described in the next chapter.
8287
- `onBeforeTest` - A function called before the test and component render.
8388
It should perform any environment tweaks necessary for the defined test.
8489
- `onBeforeSnapshot` - A function called after the component is rendered
85-
and before its comparison against the snapshot.
90+
and before its comparison against the snapshot.
8691
- `props` - The properties passed to the component in the defined
8792
test scenario.
8893

@@ -114,8 +119,8 @@ ordered alphabetically under the category of color.
114119

115120
```
116121

117-
- When possible, try to re-use globally predefined `propTests`
118-
for visual tests,located in `tests/playwright/propTests`.
122+
- When possible, try to re-use globally defined `propTests`
123+
for visual tests, located in `tests/playwright/propTests`.
119124

120125
- Naming convention for propTests `name` property should follow this pattern:
121126

@@ -130,65 +135,150 @@ for visual tests,located in `tests/playwright/propTests`.
130135
- For all possible combinations of multiple `propTests` should be used
131136
function `mixPropTests`.
132137

133-
## Test Structure Example
138+
## Templates
139+
140+
### Template for `<ComponentName>.story.tsx`
141+
142+
```tsx
143+
import React from 'react';
144+
import type { HTMLAttributes } from 'react';
145+
import { ComponentName } from '..';
146+
147+
// Types for story component will be improved when we have full TypeScript support
148+
type ComponentForTestProps = HTMLAttributes<HTMLDivElement>;
149+
type ComponentForRefTestProps = ComponentForTestProps & {
150+
testRefAttrName: string;
151+
testRefAttrValue: string;
152+
};
153+
154+
export const ComponentForTest = ({
155+
...props
156+
} : ComponentForTestProps) => (
157+
<ComponentName
158+
requiredPropA="value-a"
159+
requiredPropB="value-b"
160+
{...props}
161+
/>
162+
);
163+
164+
// Story for `ref` prop, if applicable
165+
export const ComponentForRefTest = ({
166+
testRefAttrName,
167+
testRefAttrValue,
168+
...props
169+
} : ComponentForRefTestProps) => {
170+
const ref = useRef<HTMLDivElement>(undefined);
171+
172+
useEffect(() => {
173+
ref.current?.setAttribute(testRefAttrName, testRefAttrValue);
174+
}, [testRefAttrName, testRefAttrValue]);
175+
176+
return (
177+
<Component
178+
{...props}
179+
ref={ref}
180+
/>
181+
);
182+
};
183+
184+
```
185+
186+
### Template for `<ComponentName>.spec.tsx`
187+
188+
```tsx
189+
import React from 'react';
190+
import {
191+
expect,
192+
test,
193+
} from '@playwright/experimental-ct-react';
194+
import { propTests } from '../../../../tests/playwright';
195+
import { ComponentNameForTest } from './ComponentName.story';
134196

135-
```jsx
136197
test.describe('ComponentName', () => {
137-
test.describe('base', () => {
138-
test.describe('visual', () => {
139-
[
140-
// propTests and mix of propTests
141-
].forEach(({
142-
name,
143-
onBeforeTest,
144-
onBeforeSnapshot,
145-
props,
146-
}) => {
147-
test(name, async ({
148-
mount,
149-
page,
150-
}) => {
151-
if (onBeforeTest) {
152-
await onBeforeTest(page);
153-
}
154-
155-
const component = await mount(
156-
<ComponentName
157-
{...props}
158-
/>,
159-
);
160-
161-
if (onBeforeSnapshot) {
162-
await onBeforeSnapshot(page, component);
163-
}
164-
165-
const screenshot = await component.screenshot();
166-
expect(screenshot).toMatchSnapshot();
198+
test.describe('base', () => {
199+
test.describe('visual', () => {
200+
[
201+
...propTests.defaultComponentPropTest,
202+
// ...propTests.propTestA,
203+
// ...mixPropTests([
204+
// ...propTests.propTestX,
205+
// ...propTests.propTestY,
206+
// ]),
207+
].forEach(({
208+
name,
209+
onBeforeTest,
210+
onBeforeSnapshot,
211+
props,
212+
}) => {
213+
test(name, async ({
214+
mount,
215+
page,
216+
}) => {
217+
if (onBeforeTest) {
218+
await onBeforeTest(page);
219+
}
220+
221+
const component = await mount(
222+
<ComponentNameForTest
223+
{...props}
224+
/>,
225+
);
226+
227+
if (onBeforeSnapshot) {
228+
await onBeforeSnapshot(page, component);
229+
}
230+
231+
const screenshot = await component.screenshot();
232+
expect(screenshot).toMatchSnapshot();
233+
});
234+
});
167235
});
168-
});
169-
});
170236

171-
test.describe('non-visual', () => {
172-
/* Non-visual tests. */
173-
});
237+
test.describe('non-visual', () => {
238+
// Test for `id` prop, if applicable
239+
test('id', async ({ mount }) => {
240+
const component = await mount(
241+
<ComponentForTest
242+
id="test-id"
243+
/>,
244+
);
245+
246+
await expect(component).toHaveAttribute('id', 'test-id');
247+
// Test the rest of internal IDs
248+
});
249+
250+
// Test for `ref` prop, if applicable
251+
test('ref', async ({ mount }) => {
252+
const component = await mount(
253+
<ComponentForRefTest
254+
testRefAttrName="test-ref"
255+
testRefAttrValue="test-ref-value"
256+
/>,
257+
);
258+
259+
await expect(component).toHaveAttribute('test-ref', 'test-ref-value');
260+
});
261+
262+
// Other non-visual tests
263+
});
174264

175-
test.describe('functionality', () => {
176-
/* Functional tests. */
265+
test.describe('functionality', () => {
266+
// Functional tests
267+
});
177268
});
178-
});
179269

180-
test.describe('formLayout', () => {
181-
test.describe('visual', () => {
182-
/* Visual tests as in `base` block. */
183-
});
270+
test.describe('formLayout', () => {
271+
test.describe('visual', () => {
272+
// Visual tests as in `base` block
273+
});
184274

185-
test.describe('non-visual', () => {
186-
/* Non-visual tests as in `base` block. */
187-
});
275+
test.describe('non-visual', () => {
276+
// Non-visual tests as in `base` block
277+
});
188278

189-
test.describe('functionality', () => {
190-
/* Functional tests as in `base` block. */
279+
test.describe('functionality', () => {
280+
// Functional tests as in `base` block
281+
});
191282
});
192-
});
193283
});
194284
```

0 commit comments

Comments
 (0)