Skip to main content

write-node-unit-tests

Write Node.js/TypeScript unit tests for the playwright-wrapper layer. Use when: creating new Jest tests, mocking Playwright API calls, testing getters/interaction/browser-control functions in isolation, improving Node.js test coverage.

소스 정보

저장소
MarketSquare/robotframework-browser
최근 소스 활동
2026년 10월 3일 11:17
감지된 SKILL.md 언어
영어
스타
661
포크
149

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
write-node-unit-tests
description
Write Node.js/TypeScript unit tests for the playwright-wrapper layer. Use when: creating new Jest tests, mocking Playwright API calls, testing getters/interaction/browser-control functions in isolation, improving Node.js test coverage.
applyTo
node/playwright-wrapper/__tests__/**
# Write Node.js Unit Tests Node.js unit tests live in `node/playwright-wrapper/__tests__/` and use **Jest + ts-jest**. Tests must be readable and test one thing at a time. Use descriptive `it()` names that read as plain English sentences. Run tests with invoke: ```bash inv utest-node inv utest-node --coverage # generates HTML report at node/coverage/index.html ``` ## File naming Name test files after the source module they test: `getters.ts` → `getters.test.ts`. ## IDE typing for Jest Every test file imports all Jest globals it uses from `@jest/globals`. If a Jest API such as `beforeEach` or `afterEach` is used without being imported, add it to the existing import line: ```typescript import { beforeEach, describe, expect, it } from '@jest/globals'; ``` Do not add a `/// <reference types="jest" />` directive — it is redundant when importing from `@jest/globals` and inconsistent with the rest of the test suite. Keep using the global `jest` object for `jest.mock()`, `jest.fn()`, and `jest.mocked(...)`. Avoid importing `jest` from `@jest/globals` in these tests, because it can lead to worse mock typing in this repo. ## Mocking external module dependencies Use `jest.mock()` at the top of the file (before imports) to replace modules with controlled fakes. ### Mocking `playwright-invoke` `findLocator` is the primary dependency in most getter/interaction functions. Replace it with a `jest.fn()` and keep `exists` real: ```typescript jest.mock('../playwright-invoke', () => ({ findLocator: jest.fn(), exists: jest.requireActual('../playwright-invoke').exists, })); import { findLocator } from '../playwright-invoke'; const mockFindLocator = jest.mocked(findLocator); ``` ### Mocking the logger Suppress log output to keep test output clean: ```typescript jest.mock('../browser_logger', () => ({ logger: { info: jest.fn(), error: jest.fn() }, })); ``` ### Mocking Playwright errors Use the real `errors` export from `playwright` — no mock needed. Throw `new errors.TimeoutError(...)` directly in test stubs. ## Creating mock locators Build a helper that returns an object with all `Locator` methods stubbed. Pass `overrides` to change just one method per test: ```typescript function makeMockLocator(overrides: Partial<{ waitFor: jest.Mock; isVisible: jest.Mock; isEnabled: jest.Mock; getAttribute: jest.Mock; isEditable: jest.Mock; isChecked: jest.Mock; elementHandle: jest.Mock; }> = {}) { return { waitFor: jest.fn().mockResolvedValue(undefined), isVisible: jest.fn().mockResolvedValue(true), isEnabled: jest.fn().mockResolvedValue(true), getAttribute: jest.fn().mockResolvedValue(null), isEditable: jest.fn().mockResolvedValue(true), isChecked: jest.fn().mockResolvedValue(false), elementHandle: jest.fn().mockResolvedValue(makeElementHandle()), ...overrides, } as unknown as Locator; } ``` ## Creating mock gRPC request objects After the ts-proto migration, generated protobuf types are plain interfaces with direct property access — no getter methods. Mock them with a plain object cast to `any`: ```typescript function makeRequest(selector = '#el', strict = false) { return { selector, strict } as any; } ``` ## Asserting on mock interactions Use `toHaveBeenCalledWith` and `toHaveBeenCalledTimes` to verify how mocks were called — this is often the primary assertion in the wrapper layer: ```typescript expect(mockFindLocator).toHaveBeenCalledWith(callOptions, '#el', false); expect(mockFindLocator).toHaveBeenCalledTimes(1); ``` Use `toBe` for primitives and reference identity checks; use `toEqual` for deep object comparison. ## Async error tests When testing rejected promises, call `expect.assertions(n)` at the top of the test. This ensures the test fails if the error path is never reached: ```typescript it('throws when element is not found', async () => { expect.assertions(1); mockFindLocator.mockRejectedValue(new Error('not found')); await expect(getElementStates(makeRequest(), {} as any)).rejects.toThrow('not found'); }); ``` ## Organising tests with nested describe Group related cases under inner `describe` blocks to make large test files navigable: ```typescript describe('getElementStates', () => { describe('when element is visible', () => { it('returns visible state', async () => { ... }); }); describe('when element times out', () => { it('returns detached state', async () => { ... }); }); }); ``` ## Sequencing multiple `evaluate` calls When a function calls `elementHandle.evaluate()` multiple times, use `mockResolvedValueOnce` chaining: ```typescript const elementHandle = { evaluate: jest.fn() .mockResolvedValueOnce(false) // first call: 'selected' in e .mockResolvedValueOnce(true), // second call: document.activeElement === e }; ``` ## Test structure ```typescript describe('functionName', () => { beforeEach(() => { jest.clearAllMocks(); }); it('returns detached state when element times out', async () => { const locator = makeMockLocator({ waitFor: jest.fn().mockRejectedValue(new errors.TimeoutError('Timeout')), }); mockFindLocator.mockResolvedValue(locator); const result = await getElementStates(makeRequest(), {} as any); expect(JSON.parse(result.json)).toBe(2); // detached }); }); ``` ## Things to avoid - Do not add comments or docstrings explaining what the test does — the test name and code should be self-explanatory. - Mock at the boundary closest to your unit. Prefer spying over full module replacement when only one method needs controlling; don't mock modules that aren't exercised by the test. - Do not assert on log messages — they are implementation details. - Do not share mutable mock state between tests; always call `jest.clearAllMocks()` in `beforeEach`.
GitHub에서 보기