@aerokit/test

中文文档
15.15.0发布于 2026/11/04EPL-2.0ES Module零依赖构建来源已验证近 30 天下载 2271

Generic Playwright runner that executes an intent module's generated .test manifest against a running Eclipse Dirigible instance

原始使用文档
文档目录3 节
  1. Use from a module
  2. Flows per entity
  3. Custom UI hooks

@aerokit/test

Publishing note: every release publishes this folder as @aerokit/test, versioned with the platform. Consumers depend on @aerokit/test and import @aerokit/test / @aerokit/test/fixtures. A harness written against the earlier @aerokit/sdk/test subpath keeps resolving while @aerokit/sdk still ships it, but @aerokit/sdk is being deprecated: switch the import and the dependency to @aerokit/test.

Generic Playwright runner that executes an intent module's generated <name>.test manifest against a running Eclipse Dirigible instance. The manifest (emitted by the AppTestIntentGenerator in engine-intent, alongside .edm/.model/.bpmn/...) says WHAT the app promises — entities, layouts, fields, seeds, languages; this package knows HOW to verify a promise against the generated Harmonia UI and the reused REST controllers. So one runner, versioned alongside the templates whose markup it drives, checks every generated app the same way.

This is the app-integration-test surface (drives a browser + the REST layer from outside the instance). It is distinct from @aerokit/sdk/junit, the in-instance server-side unit-test facade.

Use from a module

A module keeps a tiny test/ harness at its repo root (outside the published project folder, so it never enters the registry or the npm package):

// test/app.spec.js
import { fileURLToPath } from 'node:url';
import { runTest } from '@aerokit/test';

runTest(fileURLToPath(new URL('../<project>/<name>.test', import.meta.url)));
// test/package.json
{
  "type": "module",
  "scripts": { "test": "playwright test" },
  "devDependencies": { "@aerokit/test": "*", "@playwright/test": "^1.45" }
}
BASE_URL=http://localhost:8080 npm test        # defaults: admin/admin, installed Chrome
APPTEST_SHELL=1 npm test                       # also assert the shared-shell menu item
APPTEST_A11Y=strict npm test                   # fail on serious/critical accessibility violations

Env: BASE_URL (default http://localhost:8080), APPTEST_USERNAME/APPTEST_PASSWORD (default admin/admin), APPTEST_SHELL (opt-in shared-shell flow), APPTEST_A11Y (report default, strict, off - the accessibility flow).

Flows per entity

  • list — the shell breadcrumb naming the list by its plural label, the declared columns, and (when expectSeedData) a row. An entity whose manifest carries list renders exactly those columns, in order, and a field left off it must have no header at all; without list the columns are the fields whose major is not false.
  • crud — UI create → filter → row appears; edit via the row menu → save (a document saves in place, so the walk leaves through Cancel); delete via the row menu → confirm → gone. A walk that fails halfway still removes its test row over REST. An entity without a route (a composition child with no page of its own) has no list, crud, multilingual or shell flow.
  • rest — the same CRUD over the generated Java controllers via APIRequestContext (isolates backend vs UI failures), asserting the manifest's field names bind and delete yields 404 — or, for an entity a whenDeleted: refuse process guards, that the refused record is still served.
  • multilingual — switch the shared language key, reload, a seeded row shows its translated name. The assertion waits for the list's own data GET and allows 15 s; a failure names the Accept-Language that GET sent, whether its body carried the expected word, and the table's first row, so a slow render reads differently from a missing translation.
  • my — the personal (my) surface WIRE contract, when the manifest marks an entity personal: create through the scoped <Entity>MyController (owner FK forced server-side), every sensitive field null on the personal wire, the own row in the personal list, foreign rows 404 (when the owner relation is optional), own-row delete. Skips with a pointer when the test user has no identity mapping (seed the dev identity row). Personal UI parity (resolved labels, chat, calendar on the Personal shell) is deliberately not asserted yet — it tracks the personal-template parity fixes.
  • shell (opt-in) — the shared application shell's nav item opens the module SPA in its iframe.
  • a11y — axe-core scans the list page and the create form against WCAG 2.1 A/AA and attaches each page's result (axe-<Entity>-<page>.json) to the test report. APPTEST_A11Y=report (default) only reports - a module's first release; strict also fails on a serious or critical violation; off skips it. Needs @axe-core/playwright in the harness devDependencies (the flow skips itself without it). Contrast against Harmonia's own --primary is not counted in strict mode: the default palette misses 4.5:1 there, which the component library fixes; every other contrast failure is.

Test records carry an APPTEST- prefix and are removed in teardown; seed data is never mutated. A field the app owns rather than the user — a number: series, a calculated or readOnly: value — is never written or flipped by a flow (the manifest marks it readOnly). A relation the form renders read-only — a calculatedActionOnUpdate, a platform-owned FK — carries the same flag and is never picked in the UI. A relation fixed after create (fixedAfterCreate: true) carries readOnlyOnEdit: it is picked on create and never changed by an edit, which the generated app answers with a 409. A field declaring a pattern: gets a value of that shape rather than the marker string. An optional pattern: field whose shape none of the sample candidates has (an IBAN, say) is left empty; a required one keeps the marker, so the controller's 400 names the field. A dependsOn relation is sampled to match its trigger, as the form narrows it: with filterBy alone the trigger is the row the dependent's filterBy names; with valueFrom the dependent is the row whose filterBy (or key) equals the trigger row's valueFrom. Relations an agree check names are picked so their targets agree; a composite uniqueKeys entry is given a relation combination no live row carries, and the rest and crud flows skip with the key named when every combination tried is taken. A slots entity's start (the manifest's slots window) is a random grid start inside the hours, on an open weekday of a far-future day, so the server's slot rules (a 400 off the window, a 409 for a booked slot) never refuse it; the REST requests name UTC as their X-Time-Zone, the zone it is computed in. A relation carrying pickable is sampled only from the rows its rule admits — the chooser offers no other, so a record built from one would never save — and one carrying forbiddenTarget only from rows the record's own forbidWhen does not refuse, which is what the REST flow needs, having no picker to read. When no row is left, a required relation fails naming the rule's own message rather than "no rows". A relation whose target is labelled by a stampOn: issue number carries labelDocumentNumber: the picker shows every row not issued yet as the same "(draft)", so the sample is a stamped row, picked by its number; with none stamped yet an optional relation is left unset and a required one picks "(draft)".

Custom UI hooks

runTest(manifest, { extend }) (runAppTest remains a deprecated alias):

  • widgets: { '<widget>': async (page, field, value) => ... } — custom widget fillers.
  • entities: { <Entity>: { skip: ['delete'], beforeCreate, afterCreate } } — per-entity hooks.

Hand-written specs live in test/custom/*.spec.js and import @aerokit/test/fixtures (pre-logged-in test, an api client, expect) so login/baseURL/cleanup come free.

版本与发布记录

@aerokit/test 共发布 14 个版本,首次发布于 2026/10/23;过去 12 个月发布了 14 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
15.15.0latest2026/11/043 天前
15.14.02026/11/044 天前
15.13.02026/11/034 天前
15.12.02026/11/025 天前
15.11.02026/10/317 天前
15.10.02026/10/318 天前

关于 @aerokit/test 的常见问题

@aerokit/test 是什么?

Generic Playwright runner that executes an intent module's generated .test manifest against a running Eclipse Dirigible instance

如何安装 @aerokit/test?

在项目目录运行 npm install @aerokit/test,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@playwright/test(>=1.45)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aerokit/test 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/aerokit__test 是否存在。

@aerokit/test 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aerokit/test 可以商用吗?

@aerokit/test 使用 EPL-2.0 许可。声明的许可为 EPL-2.0,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@aerokit/test 的下载量有多少?

根据 npm 官方下载统计,@aerokit/test 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,271 次,日均约 76 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aerokit/test 还在维护吗?

最新版本 15.15.0 发布于 2026/11/04(3 天前);至今共发布 14 个版本,其中过去 12 个月发布了 14 个;首次发布于 2026/10/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。