Skip to content

Commit d7f7e04

Browse files
committed
Update TESTING.md with Jest clarification and Playwright selector guide
1 parent e001787 commit d7f7e04

1 file changed

Lines changed: 36 additions & 8 deletions

File tree

docs/TESTING.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,20 +283,24 @@ jest.mock('path/to/module');
283283
* We create a mock for any information passed into the function that is being tested
284284
* and if the function returns a result we create a mock to test the result
285285
*/
286-
const mockParam = "mockParam"
287-
const mockReturnValue = "mockModuleValue"
286+
const mockParam = "mockParam";
287+
const mockReturnValue = "mockModuleValue";
288288

289289
/**
290290
* use .mockResolvedValue when handling async/await modules that return values
291291
* use .mockReturnValue when handling non async/await modules that return values
292292
*/
293-
(module as jest.Mock).mockResolvedValue(mockReturnValue);
293+
describe('functionUnderTest', () => {
294+
it('returns mocked module value and calls dependency correctly', async () => {
295+
(mockedDep as jest.Mock).mockResolvedValue(mockReturnValue);
294296

295-
const result = await functionUnderTest(mockParam);
297+
const result = await functionUnderTest(mockParam);
296298

297-
expect(result).toBe(mockReturnValue);
298-
expect(module).toBeCalledTimes(1);
299-
expect(module).toBeCalledWith(mockParam);
299+
expect(result).toBe(mockReturnValue);
300+
expect(mockedDep).toHaveBeenCalledTimes(1);
301+
expect(mockedDep).toHaveBeenCalledWith(mockParam);
302+
});
303+
});
300304
```
301305

302306
Use namespace imports when you want to import everything a module exports under a single name.
@@ -474,4 +478,28 @@ jest.spyOn(Array.prototype, 'includes').mockImplementation(function(value) {
474478

475479
### Playwright (E2E) Testing Guide
476480

477-
TODO
481+
##### Component Selection Hierarchy
482+
483+
Use this priority order for selecting elements in Playwright tests:
484+
485+
1. Prefer `getByRole()` — use semantic roles that reflect how users interact
486+
```typescript
487+
await page.getByRole('button', { name: 'Submit' }).click();
488+
```
489+
If a meaningful ARIA role is not available, fall back to accessible text selectors (next point).
490+
491+
2. Use accessible text selectors — when roles don't apply, target user-facing text
492+
```typescript
493+
await page.getByLabel('Email').fill('user@example.com');
494+
await page.getByPlaceholder('Enter your name').fill('John');
495+
await page.getByText('Welcome back').isVisible();
496+
```
497+
498+
3. Only use `data-testid` — when elements have no stable user-facing text
499+
```typescript
500+
// For icons, toggles, or dynamic content without text
501+
await page.getByTestId('menu-toggle').click();
502+
await page.getByTestId('loading-spinner').isVisible();
503+
```
504+
505+
This hierarchy mirrors how users actually interact with your application, making tests more reliable and meaningful.

0 commit comments

Comments
 (0)