1- # Playwright Testing Structure
1+ ## Playwright Testing Structure
22
33This document outlines the structure and guidelines for writing Playwright
44tests, ensuring consistency and maintainability throughout the codebase.
@@ -7,8 +7,10 @@ tests, ensuring consistency and maintainability throughout the codebase.
77
88Playwright tests are organized into multiple files and folders, each
99serving a specific purpose.
10+
1011The 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
2225the component itself.
@@ -29,12 +32,13 @@ described below.
2932in tests. These components should be functional without requiring any
3033properties to be passed from the tests.
3134
32- ## <ComponentName >.spec.tsx File Structure
35+ ## File Structure of ` <ComponentName>.spec.tsx `
3336
3437Playwright tests follow a structured format to ensure readability
3538and scalability. Each displayed level represents a ` test.describe(...) ` block.
3639The 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
6065of the tested component and whether a predefined test block is applicable
6166in a specific case.
6267
63- ## <ComponentName >.story.tsx File Structure
68+ ## File Structure of ` <ComponentName>.story.tsx `
6469
6570The ` <ComponentName>.story.tsx ` file should include all component variants
6671tested in ` <ComponentName>.spec.tsx ` . Components should be organized
67- in the following order:
72+ in the following order:
6873
69741 . Component for normal tests (` <ComponentName>ForTest ` )
70752 . Component for ` ref ` attribute tests (` <ComponentName>ForRefTest ` )
@@ -75,14 +80,14 @@ in the following order:
7580
7681Each test case should follow the properties defined in the ` PropTest ` type.
7782This 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
8792test 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
131136function ` 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
136197test .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