Skip to content

Testing

Updated View as Markdown

Test your application definitions at the controller boundary and your replacement components in a browser. Core helpers do not replace full routing/CSRF/browser verification.

Japa helpers

@rikology/pupitre/testing exports visitResource, submitForm, runAction, visitPage, assertSpecHas, assertVisible and assertHidden.

import { visitResource, submitForm, assertSpecHas, assertVisible } from '@rikology/pupitre/testing'

const rendered = await visitResource('posts', 'create', { user, panel: 'admin' })
assertSpecHas(rendered.spec, { type: 'Text' })
assertVisible(rendered.spec, rendered.state, 'f.title')

const result = await submitForm('posts', 'create', { title: 'A post' }, { user })
result.assertOk()

Create a real authenticated model user and boot your test application with the Pupitre provider, Lucid and Bouncer first. Helpers build a test HTTP context and execute actual controllers, including policies, tenant scope, validation and hooks.

Helper Signature
visitResource `(slug, ‘list’
submitForm `(slug, ‘create’
runAction (slug, name, options); options may contain record, ids and data
visitPage (slug, options) for custom pages
assertSpecHas (spec, { type, name?, props? }) matches type and optional prop subset
assertVisible, assertHidden (spec, state, elementName) evaluates compiled visibility

Options require user; optional app, tenant, panel, query, record set context. Panel defaults to admin. Edit submission requires a record (model instance or ID). Action 403 throws; 422 returns errors. assertErrors({ title: 'required' }) matches case-insensitive substrings in field messages.

Inspect compiled element names rather than assuming a model attribute is always the element ID in nested layouts/repeaters. Deferred props may still need resolution in integration assertions.

Playwright helpers

import { pupitreTest as test, expect } from '@rikology/pupitre-ui/testing'

test.use({ pupitre: { panelPath: '/admin', email: 'staff@example.com', password: 'test-password' } })

test('creates a post', async ({ admin, page }) => {
  await admin.login()
  await admin.gotoResource('posts', '/create')
  await admin.fillField('Title', 'A new post')
  await page.getByRole('button', { name: 'Create', exact: true }).click()
  await expect(page).toHaveURL(/posts/)
})

The fixture supplies AdminPage: login, gotoResource, fillField, field, selectOption, openAction, submitModal, expectToast, row and table. Configure basePath for URL tenancy and landingPath when login should wait for a non-default destination. The helpers use accessible labels/roles and support both native and searchable selects.

Use isolated test databases. Cover allowed/denied policies, cross-tenant IDs, 422 errors, hooks, relation ownership, uploads, action modal transport and queue identity reload. Freeze time for timezone tests, with both UTC and a DST zone. Avoid snapshotting incidental HTML; compiled spec snapshots and schema validation assert the actual wire contract.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close