mcpField
J
japa

japa

github.com/lncitadorTesting & quality745 installsUpdated 25w ago
Repository

About japa

A Japa testing skill for AdonisJS projects, covering unit, browser, API, and console test setup, along with database cleanup and test-double practices.

Key features

  1. 01Configure unit and browser suites
  2. 02Run selected suites, files, or tests
  3. 03Set up authentication and test environment
  4. 04Manage database state between tests
  5. 05Guide API, browser, and console tests
  6. 06Apply test-double and cleanup practices

Use cases

  1. 01Configure test suites in an AdonisJS project
  2. 02Run a selected set of tests
  3. 03Isolate database state between tests
  4. 04Write API or browser tests

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/lncitador/adonisjs-maestro --skill japa

After installing, type/japain Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

agents/
openai.yaml
references/
api-tests.md
browser-tests.md
console-tests.md
test-doubles.md
runbooks/
resource-tests.md

Details

From the project README

Japa Testing Skill

AdonisJS uses Japa — a backend testing framework built and maintained by the AdonisJS team. Unlike Jest/Vitest, Japa runs natively in Node.js without transpilers and has plugins specifically designed for backend testing.


Suites and structure

tests/
├── bootstrap.ts          # plugins, hooks, suite config
├── unit/                 # fast in-process tests
└── browser/              # end-to-end with Playwright

Suites defined in adonisrc.ts:

tests: {
  suites: [
    { files: ['tests/unit/**/*.spec.ts'], name: 'unit', timeout: 2000 },
    { files: ['tests/browser/**/*.spec.ts'], name: 'browser', timeout: 300000 },
  ],
  forceExit: false,
}

bootstrap.ts

Hypermedia/Inertia starter kits (browser + unit suites):

import { assert } from '@japa/assert'
import app from '@adonisjs/core/services/app'
import type { Config } from '@japa/runner/types'
import { pluginAdonisJS } from '@japa/plugin-adonisjs'
import testUtils from '@adonisjs/core/services/test_utils'
import { browserClient } from '@japa/browser-client'
import { authBrowserClient } from '@adonisjs/auth/plugins/browser_client'
import { sessionBrowserClient } from '@adonisjs/session/plugins/browser_client'

export const plugins: Config['plugins'] = [
  assert(),
  pluginAdonisJS(app),
  browserClient({ runInSuites: ['browser'] }),
  sessionBrowserClient(app),
  authBrowserClient(app),
]

export const runnerHooks: Required<Pick<Config, 'setup' | 'teardown'>> = {
  setup: [() => testUtils.db().migrate()],
  teardown: [],
}

export const configureSuite: Config['configureSuite'] = (suite) => {
  if (['browser', 'functional', 'e2e'].includes(suite.name)) {
    return suite.setup(() => testUtils.httpServer().start())
  }
}

API starter kit (adds apiClient plugins):

import { apiClient } from '@japa/api-client'
import { authApiClient } from '@adonisjs/auth/plugins/api_client'
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
import type { Registry } from '../.adonisjs/client/registry/schema.d.ts'

declare module '@japa/api-client/types' {
  interface RoutesRegistry extends Registry {}
}

export const plugins: Config['plugins'] = [
  assert(),
  pluginAdonisJS(app),
  apiClient(),
  sessionApiClient(app),
  authApiClient(app),
]

.env.test — always required for auth in tests:

SESSION_DRIVER=memory

Running tests

node ace test                           # all suites
node ace test unit                      # specific suite
node ace test browser

node ace test --files="posts/index"     # specific file
node ace test --files="posts/*"         # folder wildcard
node ace test --tests="can list posts"  # exact test title
node ace test --groups="Posts index"    # group name

node ace test --watch                   # re-run on change
node ace test --bail                    # stop on first failure
node ace test --failed                  # re-run only failed tests

Database state — between each test

Choose one approach per group:

// Global transactions (faster — rolls back, nothing persisted)
group.each.setup(() => testUtils.db().withGlobalTransaction())

// Truncation (actual delete — slower but more compatible)
group.each.setup(() => testUtils.db().truncate())

Run migrations once before all tests in bootstrap.ts:

runnerHooks.setup = [
  () => testUtils.db().migrate(),
  () => testUtils.db().seed(),  // optional
]

Multiple connections:

() => testUtils.db().migrate()
() => testUtils.db('tenant').migrate()

References

TopicFile
API tests — client.visit(), assertions, authreferences/api-tests.md
Browser tests — Playwright, forms, loginAs, cookiesreferences/browser-tests.md
Console tests — Ace commands, raw mode, promptsreferences/console-tests.md
Test doubles — fakes, swaps, time, sinonreferences/test-doubles.md

Runbooks

FeatureFile
Full test suite for a CRUD resourcerunbooks/resource-tests.md

Anti-Patterns

WrongCorrect
No DB cleanup between testswithGlobalTransaction() or truncate() in group.each.setup
SESSION_DRIVER not set to memoryRequired in .env.test for loginAs to work
Using fakes without using or .restore()using fake = mail.fake() for auto-restore
sinon.restore() in group.each.setupsinon.restore() in group.each.teardown
redis.flushdb() in group.each.setupIn group.each.teardown (clear after, not before)
Mocking DB queriesHit the real test DB — mocks miss constraint errors
record() in committed testOnly for generating code — replace with visit()
Prompt trap without exact title matchTitles are case-sensitive — copy exactly from command

Comments

Markdown supported