Skip to content

dvegap95/page-component-test-object

Repository files navigation

Page Component Object (PCO)

A toolset for behavioral integration tests in React — scoped TestObjects (*.to.ts / *.to.tsx in __pco__) hold queries, interactions, and (where supported) assertions so specs describe what the user does, not how to find every node in the tree.

New here? Copy Install → follow Getting started (Level 1). Skim What it is for context. For the booking-flow story: vision.

Status: 0.1.2 on npm — @page-component-object/*. Stable: Vitest, Jest, Storybook (MSW-backed view tests). Shipped: Cypress PCOChainable / findBy*. In progress (0.2.x): unified cross-runner TestObjects — resolver model, Cypress adoption.

Quick example

// Home.to.tsx — view test object: queries + render + API mocks
export class HomeViewTestObject extends BaseViewTestObject {
  get heading() {
    return this.context.getByRole('heading', { name: /items/i });
  }

  get itemLinks() {
    return this.context.getAllByRole('link');
  }

  async render() {
    return this.app.renderView(<Home items={this.items} loading={false} />, {
      route: '/',
      routePath: '/',
    });
  }
}
// Vitest behavioral test — elements from getters are PCO targets
const view = new HomeViewTestObject();
await view.render();
expect(view.itemLinks).toHaveLength(3);
await view.itemLinks[0].userClick();

See getting started for the full walkthrough (Level 1 → 3).

Install

pnpm add @page-component-object/core @page-component-object/queries @page-component-object/msw \
  @page-component-object/react @page-component-object/router-react \
  @page-component-object/adapter-vitest

Add @page-component-object/adapter-jest, @page-component-object/adapter-storybook, or @page-component-object/adapter-cypress for other runners. Peer dependencies and per-runner bundles: install.

What it is

Integration tests tend to accumulate inline query chains and interaction blocks — repeated setup and selectors scattered across specs instead of behavior.

In a focused test (one dialog, one primary action), a direct query is often enough:

await screen.findByRole('button', { name: 'Accept' }).click();

That breaks down in full integration views: another Accept on a card, a sticky bar, or a row action added later can make the same global query ambiguous. Teams then reach for nested within() scopes — workable, but duplicated across every spec that touches the same flow.

PCO applies Page Object thinking at the component and view level: scoped test objects beside the feature hold query knowledge once. For an already-open dialog:

await view.addItemButton.userClick(); // view action that opens the modal
await view.confirmAdditionModal.acceptButton.userClick(); // scoped to that dialog

confirmAdditionModal scopes queries to the Confirm Addition dialog — not whichever Accept matches first on the page. Opening the modal stays a view action (or a view intent like confirmAddition() if you compose the whole flow). The modal test object does not need to know what opened it.

Specs stay focused on observable behavior: render a view, act through intents or scoped targets, assert outcomes.

Supporting the full integration surface

Behavioral view tests need more than DOM helpers. PCO bundles the surrounding harness:

Concern Where it lives
Scoped queries & user actions ComponentTestObject / view *.to.*
Scenario-focused payloads DataFactory — strip fields irrelevant to the test (project structure)
Render, router, storage BaseViewTestObject — view-owned harness (getting started — Level 3)
HTTP mocks & request assertions ApiTestObject + MSW — mock the response you expect, assert what was sent (HTTP boundary, matchers)

Across the testing stack

That ecosystem is usually built first for one runner (Vitest or Jest). Then the same intents show up again — Storybook play functions, Cypress E2E — redefining queries and procedures for the same widgets even when files sit beside the component.

PCO abstracts and widens the Page Component Object pattern through adapters. Today: Vitest, Jest, and Storybook share MSW-backed view test objects; Cypress reuses DOM getters (chainables shipped in 0.1.2). Direction (0.2.x): unified TestObject definitions across runners via the resolver model — so the same __pco__ surface runs everywhere without parallel getter copies. Cross-runner reuse extends the central contract; it is not the reason to adopt scoped TestObjects in the first place.

Vision

A booking-flow spec should read like the scenario — not like fixture assembly plus DOM chains. PCO keeps Given / When / Then in the test; query knowledge, mock wiring, and widget mechanics live in test objects you own.

Full walkthrough — verbose spec vs PCO, feature by feature: docs/vision.md.

Technical context

Area In this repo
Monorepo pnpm workspaces, Turborepo, tsup builds across scoped packages
Library design Adapter pattern — one TestObject surface, four test runners
Test layering Query → primitive interaction → domain intent (philosophy)
Selectors Testing Library roles and accessible names; Cypress via @testing-library/cypress
HTTP boundary MSW v2 registry — handlers shared between node tests and Storybook (http-boundary)
Test data DataFactory — scenario-focused payloads, separate from ApiTestObject
Widget reuse Composable presets (e.g. @page-component-object/preset-mui) in view test objects
API assertions @semantic-matchers integration for Vitest/Jest spy matchers

Packages

Package Description
@page-component-object/page npm org landing — links to all packages (not a runtime dependency)
@page-component-object/core Types, DataFactory, App singleton, runtime injection
@page-component-object/queries ComponentTestObject — RTL queries + user agent
@page-component-object/msw MSW v2 API mock registry and session helpers
@page-component-object/react BaseViewTestObject, BaseAppManager
@page-component-object/router-react React Router v6/v7 test shell (MemoryRouter, Routes, useNavigate)
@page-component-object/preset-mui MUI widget test objects (Button, Select, Snackbar, TimePicker, TableRow, …)
@page-component-object/adapter-vitest Vitest lifecycle + user agent
@page-component-object/adapter-jest Jest lifecycle + user agent
@page-component-object/adapter-storybook createStoryPlay, storyParameters, pcoViewLoader, MSW bridge
@page-component-object/adapter-cypress Cypress runtime, CypressComponentTestObject, PCOChainable

Demo apps (not published)

Fictitious catalog domain under apps/demo-shared.

App Runner What it demonstrates
apps/vitest-demo Vitest + MSW Shallow view + full-app navigation
apps/jest-demo Jest + MSW Same behavioral specs as Vitest
apps/storybook-demo Storybook MSW stories + MUI preset play demos
apps/cypress-demo Cypress E2E TestObject getters bound to a live app

Documentation

Doc Topic
Getting started Level 1 → 3 progressive onboarding — start here after install
Vision Booking-flow walkthrough — verbose spec vs PCO, design goals
Install npm packages, peers, compatibility matrix
Why PCO (deep dive) Duplication diagram, layer model, when it pays off
HTTP boundary MSW, ApiTestObject, request vs response testing
Design principles Runtime contracts, escape hatches
Cross-runner tutorial One view in Vitest, Storybook, Cypress
Portability What travels vs runner-native
Resolver model rootResolver, PCOTarget architecture
Cypress adoption Chainables, E2E path, Playwright comparison
Project structure __pco__ layout, factories vs API mocks
MSW in tests vs Storybook Shared handlers, story parameters
MUI preset Widget test objects
API matchers toHaveBeenLastCalledWithUrl for MSW spies
Philosophy Query → primitive → intent
When not to use Honest scope boundaries
Changelog Version history

Development

Requirements: Node 20+, pnpm 9+

pnpm install
pnpm build
pnpm test                    # Vitest + Jest demos (excludes slow Cypress E2E)
pnpm --filter @page-component-object/cypress-demo test   # Cypress E2E
pnpm test:consumer-smoke     # tarball install smoke (CI)
pnpm --filter @page-component-object/storybook-demo storybook

Monorepo tooling: pnpm workspaces, Turborepo, tsup for package builds.

Maintainers: release workflow in .github/PUBLISH.md; architecture notes in .github/PLAN.md.

Roadmap

Area Status
Vitest / Jest / Storybook + MSW Stable in 0.1.2
Cypress PCOChainable + findBy* Shippedcypress-adoption
Unified cross-runner TO definitions (resolver model) In progress 0.2.xresolver-model
API matchers (toHaveBeenLastCalledWithUrl) Stable on Vitest/Jest — matchers
Cypress chainable matchers Planned
More UI presets (@page-component-object/preset-*) Planned
Playwright adapter Research spike — both E2E paths open

Related

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages