Skip to content

Commit 9a62aa4

Browse files
committed
Add writing-tests-guidelines.md (#612)
1 parent a7d1e91 commit 9a62aa4

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Playwright Testing Structure
2+
3+
This document outlines the structure and guidelines for writing Playwright
4+
tests, ensuring consistency and maintainability throughout the codebase.
5+
6+
## Folder Structure
7+
8+
Playwright tests are organized into multiple files and folders, each
9+
serving a specific purpose.
10+
The complete component structure should be as follows:
11+
12+
<ComponentName>/
13+
├── __tests__/
14+
│ ├── <ComponentName>.spec.tsx-snapshots/
15+
│ ├── _propTests/
16+
│ │ ├── <propTestName>.ts
17+
│ ├── <ComponentName>.spec.tsx
18+
│ ├── <ComponentName>.story.tsx
19+
├── ...rest component files
20+
21+
- `<ComponentName>` - Root folder of the component, named after
22+
the component itself.
23+
- `<propTestName>.ts` - Defines local test property combinations used only
24+
within the context of the tested component. Global tests shared across
25+
the project are located in `tests/playwright/propTests`.
26+
- `<ComponentName>.spec.tsx` - Contains all tests, structured as
27+
described below.
28+
- `<ComponentName>.story.tsx` - Includes all component definitions used
29+
in tests. These components should be functional without requiring any
30+
properties to be passed from the tests.
31+
32+
## <ComponentName>.spec.tsx File Structure
33+
34+
Playwright tests follow a structured format to ensure readability
35+
and scalability. Each displayed level represents a `test.describe(...)` block.
36+
The structure consists of:
37+
38+
<ComponentName>/
39+
├── base/
40+
│ ├── visual/
41+
│ │ ├── fullPage/
42+
│ ├── non-visual/
43+
│ ├── functionality/
44+
├── formLayout/
45+
│ ├── visual/
46+
│ ├── non-visual/
47+
│ ├── functionality/
48+
49+
- `<ComponentName>` - Groups all tests for the tested component.
50+
- `base` - Contains component tests without any additional layout.
51+
- `visual` - Tests that compare the component state against snapshots.
52+
- `fullPage` - Subgroup of visual tests that must be performed
53+
on a full-scale page.
54+
- `non-visual` - Validates non-functional properties (e.g., `id` or `ref`).
55+
- `functionality` - Validates properties that affect the component's behavior
56+
(e.g., `onChange`).
57+
- `formLayout` - Contains tests for the component wrapped in `<FormLayout/>`.
58+
59+
Test block categories can be expanded or removed depending on the nature
60+
of the tested component and whether a predefined test block is applicable
61+
in a specific case.
62+
63+
## <ComponentName>.story.tsx File Structure
64+
65+
The `<ComponentName>.story.tsx` file should include all component variants
66+
tested in `<ComponentName>.spec.tsx`. Components should be organized
67+
in the following order:
68+
69+
1. Component for normal tests (`<ComponentName>ForTest`)
70+
2. Component for `ref` attribute tests (`<ComponentName>ForRefTest`)
71+
3. Component for layout tests (`<ComponentName>ForLayoutTest`)
72+
4. Components for other type of tests that follow conventions above.
73+
74+
## Anatomy of Test Case
75+
76+
Each test case should follow the properties defined in the `PropTest` type.
77+
This type includes the properties `name`, `onBeforeTest`, `onBeforeSnapshot`,
78+
and `props`, which define the component setup for the actual test case.
79+
80+
- `name` - The name of the test case, following the naming conventions
81+
described in the next chapter.
82+
- `onBeforeTest` - A function called before the test and component render.
83+
It should perform any environment tweaks necessary for the defined test.
84+
- `onBeforeSnapshot` - A function called after the component is rendered
85+
and before its comparison against the snapshot.
86+
- `props` - The properties passed to the component in the defined
87+
test scenario.
88+
89+
## Formatting and code style
90+
91+
- When writing visual tests, the test for the default component properties
92+
should always be placed first. This test is always represented by
93+
`propTests.defaultComponentPropTest`, defined in the global `propTests`.
94+
95+
- Prop test variants should be sorted alphabetically. If there are multiple
96+
prop tests like `feedbackColor`, `neutralColor`, etc., they should still be
97+
ordered alphabetically under the category of color.
98+
99+
```jsx
100+
test.describe('blockName', () => {
101+
[
102+
...propTests.aPropTest,
103+
...propTests.bPropTest,
104+
...propTests.cPropTest,
105+
].forEach(({
106+
name,
107+
onBeforeTest,
108+
onBeforeSnapshot,
109+
props,
110+
}) => {
111+
// Rest of test setup.
112+
});
113+
});
114+
115+
```
116+
117+
- When possible, try to re-use globally predefined `propTests`
118+
for visual tests,located in `tests/playwright/propTests`.
119+
120+
- Naming convention for propTests `name` property should follow this pattern:
121+
122+
```text
123+
someProp:string
124+
someProp:bool=true
125+
someProp:bool=false
126+
someProp:shape[flat]
127+
someProp:shape[nested]
128+
```
129+
130+
- For all possible combinations of multiple `propTests` should be used
131+
function `mixPropTests`.
132+
133+
## Test Structure Example
134+
135+
```jsx
136+
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();
167+
});
168+
});
169+
});
170+
171+
test.describe('non-visual', () => {
172+
/* Non-visual tests. */
173+
});
174+
175+
test.describe('functionality', () => {
176+
/* Functional tests. */
177+
});
178+
});
179+
180+
test.describe('formLayout', () => {
181+
test.describe('visual', () => {
182+
/* Visual tests as in `base` block. */
183+
});
184+
185+
test.describe('non-visual', () => {
186+
/* Non-visual tests as in `base` block. */
187+
});
188+
189+
test.describe('functionality', () => {
190+
/* Functional tests as in `base` block. */
191+
});
192+
});
193+
});
194+
```

0 commit comments

Comments
 (0)