Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-views-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/kitten-lynx-test-infra": patch
---

Add `KittenLynxView.url()` so Android test consumers can read the currently navigated Lynx bundle URL.
2 changes: 2 additions & 0 deletions .github/a2ui-catalog.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ For catalog navigation, keep `components` and `catalog` as route aliases that re

When a GenUI package builds a CLI or other generated artifact that another workspace package executes during its own build, declare that package's `dist/**` (or equivalent generated directory) as Turbo `build.outputs`. Without explicit outputs, cache hits can skip restoring the built CLI and leave downstream workspace bins pointing at missing files.

When `packages/genui/a2ui` generates its catalog, ensure `packages/genui/a2ui-catalog-extractor` has been built first. The `genui a2ui generate catalog` command delegates through `@lynx-js/genui-cli`, which imports `../a2ui-catalog-extractor/dist/cli.js`; clean CI runs will fail if that dist CLI is not produced before A2UI's build or API extractor script.

When implementing A2UI v0.9 functions in `packages/genui/a2ui`, keep function resolution scoped to the active catalog first, with the global `FunctionRegistry` only as an escape hatch. Dynamic component props, checks, and function-call actions should all go through the same `resolveDynamicValue` / `executeFunctionCall` path so data bindings, nested function calls, zod argument coercion from `@a2ui/web_core`, and `formatString` data-context interpolation stay consistent.

When verifying `packages/genui/a2ui-playground`, remember that `pnpm -F @lynx-js/genui-a2ui build` first runs `tsc --project tsconfig.build.json` and then regenerates catalog JSON through `build:catalog`. The playground consumes `@lynx-js/genui/a2ui` through package exports under `dist/**`, so you normally do not need a separate `tsc` step unless you intentionally skipped the package `build` step.
Expand Down
6 changes: 5 additions & 1 deletion .github/ui-judge.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
applyTo: "packages/genui/ui-judge/**/*"
---

When extending `@lynx-js/ui-judge`, keep `judgePage` as the only public runtime API until a caller needs more surface area. Callers own Playwright page setup, navigation, viewport, cookies, route mocks, and authentication. Additional dimensions should remain internal unless they are intentionally added to the package exports.
When extending `@lynx-js/ui-judge`, keep the public runtime API small and platform-specific. Playwright callers use `judgePage` and own page setup, navigation, viewport, cookies, route mocks, and authentication. Additional dimensions should remain internal unless they are intentionally added to the package exports.

When adding Android support to `@lynx-js/ui-judge`, keep the public call shape close to `judgePage`: accept the `KittenLynxView` returned by `@lynx-js/kitten-lynx-test-infra`'s `newPage()` as `page`. Callers should own the Kitten-Lynx connection, navigation, and teardown lifecycle, while UI Judge creates the internal Midscene agent adapter. For Android scoring, pass `screenshotIncluded: true` without web-only DOM requirements, and return `page.url()` through the existing result `url` field.

Keep Android-specific `@lynx-js/ui-judge` tests on Vitest rather than Playwright. Use a dedicated `test:android` script and let Playwright tests stay under `test:playwright`, so the Android emulator CI job can run UI Judge's Kitten-Lynx coverage without pulling in browser fixtures.

Midscene scoring in this package should use `aiNumber()` and return a JSON-serializable integer score from 0 to 5. Prompt text must cooperate with Midscene's `aiNumber()` parser by asking for the requested `Number` field, not a bare JSON number. Do not reintroduce letter grades or `GRADE:` output in prompts.

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:
exit 0
fi
ulimit -Sn 655350
pnpm --filter @lynx-js/ui-judge test
pnpm --filter @lynx-js/ui-judge run test:playwright

ui-judge-comment:
needs: ui-judge
Expand Down Expand Up @@ -356,6 +356,7 @@ jobs:

# 8. Run the tests
pnpm --filter @lynx-js/kitten-lynx-test-infra run test --coverage --reporter=github-actions --reporter=dot --reporter=junit --outputFile=test-report.junit.xml --coverage.reporter='json' --coverage.reporter='text' --testTimeout=50000 --no-cache --logHeapUsage --silent
UI_JUDGE_ANDROID_INTEGRATION=1 pnpm --filter @lynx-js/ui-judge run test:android --coverage --reporter=github-actions --reporter=dot --reporter=junit --outputFile=ui-judge-android-test-report.junit.xml --coverage.reporter='json' --coverage.reporter='text' --testTimeout=50000 --no-cache --logHeapUsage --silent

test-typos:
runs-on: lynx-ubuntu-24.04-medium
Expand Down
3 changes: 1 addition & 2 deletions examples/react-externals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"build": "pnpm run build:comp-lib && pnpm run build:reactlynx && rspeedy build",
"build": "pnpm run build:comp-lib && rspeedy build",
"build:comp-lib": "rslib build --config rslib-comp-lib.config.ts",
"build:comp-lib:dev": "cross-env NODE_ENV=development rslib build --config rslib-comp-lib.config.ts",
"build:reactlynx": "pnpm --filter @lynx-js/react-umd build",
"dev": "rspeedy dev"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/genui/a2ui-catalog-extractor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"scripts": {
"api-extractor": "node ../scripts/run-api-extractor.mjs",
"build": "tsc --project tsconfig.build.json",
"build:api": "tsc --project tsconfig.build.json",
"test": "rstest"
},
"dependencies": {
Expand Down
14 changes: 14 additions & 0 deletions packages/genui/a2ui-catalog-extractor/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
"outputs": [
"dist/**"
]
},
"build:api": {
"dependsOn": [],
"inputs": [
"src/**",
"bin/**",
"package.json",
"rslib.config.ts",
"tsconfig.build.json",
"tsconfig.json"
],
"outputs": [
"dist/**"
]
}
}
}
3 changes: 2 additions & 1 deletion packages/genui/a2ui-prompt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
],
"scripts": {
"api-extractor": "node ../scripts/run-api-extractor.mjs",
"build": "rslib build"
"build": "rslib build",
"build:api": "rslib build"
},
"devDependencies": {
"@microsoft/api-extractor": "catalog:",
Expand Down
17 changes: 17 additions & 0 deletions packages/genui/a2ui-prompt/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@
"outputs": [
"dist/**"
]
},
"build:api": {
"dependsOn": [],
"inputs": [
"src/**",
"../server/agent/a2ui-catalog.ts",
"../server/agent/a2ui-examples.ts",
"../server/agent/a2ui-prompt.ts",
"../server/agent/catalog/**/*.json",
"package.json",
"rslib.config.ts",
"tsconfig.build.json",
"tsconfig.json"
],
"outputs": [
"dist/**"
]
}
}
}
1 change: 1 addition & 0 deletions packages/genui/a2ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"scripts": {
"api-extractor": "node ../scripts/run-api-extractor.mjs",
"build": "tsc --project tsconfig.build.json && npm run build:catalog",
"build:api": "tsc --project tsconfig.build.json",
"build:catalog": "genui a2ui generate catalog --catalog-dir src/catalog --out-dir dist/catalog && node scripts/writeCatalogManifestIndex.js",
"test": "rstest"
},
Expand Down
13 changes: 13 additions & 0 deletions packages/genui/a2ui/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
"dist/**"
]
},
"build:api": {
"dependsOn": [],
"inputs": [
"src/**",
"styles/**",
"tsconfig.json",
"tsconfig.build.json",
"package.json"
],
"outputs": [
"dist/**"
]
},
"api-extractor": {
"dependsOn": [
"//#build",
Expand Down
3 changes: 2 additions & 1 deletion packages/genui/openui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
],
"scripts": {
"api-extractor": "node ../scripts/run-api-extractor.mjs",
"build": "tsc -p tsconfig.build.json && cp src/core/renderer.css dist/core/renderer.css"
"build": "tsc -p tsconfig.build.json && cp src/core/renderer.css dist/core/renderer.css",
"build:api": "tsc -p tsconfig.build.json"
},
"dependencies": {
"@openuidev/lang-core": "^0.2.4",
Expand Down
12 changes: 12 additions & 0 deletions packages/genui/openui/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
"outputs": [
"dist/**"
]
},
"build:api": {
"dependsOn": [],
"inputs": [
"src/**",
"tsconfig.json",
"tsconfig.build.json",
"package.json"
],
"outputs": [
"dist/**"
]
}
}
}
2 changes: 1 addition & 1 deletion packages/genui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
],
"scripts": {
"api-extractor": "node scripts/run-api-extractor.mjs",
"build": "pnpm run clean && pnpm --dir a2ui-catalog-extractor build && pnpm --dir a2ui-prompt build && pnpm --dir openui build && pnpm --dir a2ui build && tsc --project tsconfig.build.json",
"build": "pnpm run clean && tsc --project tsconfig.build.json",
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true});\""
Comment thread
PupilTong marked this conversation as resolved.
},
"dependencies": {
Expand Down
10 changes: 10 additions & 0 deletions packages/genui/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@
"a2ui-prompt/dist/**",
"openui/dist/**"
]
},
"api-extractor": {
"dependsOn": [
"//#build",
"@lynx-js/genui-a2ui#build:api",
"@lynx-js/genui-a2ui-catalog-extractor#build:api",
"@lynx-js/genui-a2ui-prompt#build:api",
"@lynx-js/genui-openui#build:api"
],
"cache": false
}
}
}
20 changes: 20 additions & 0 deletions packages/genui/ui-judge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ test('judges generated UI', async ({ page }) => {
});
```

`judgeAndroidAgent` judges an Android Lynx screen through a Kitten-Lynx page.
Callers own the Kitten-Lynx device/app lifecycle, including connection,
navigation, and teardown. The judge reads `page.url()` for the returned JSON
object, mirroring `judgePage`.

```ts
import { Lynx } from '@lynx-js/kitten-lynx-test-infra';
import { judgeAndroidAgent } from '@lynx-js/ui-judge';

const lynx = await Lynx.connect({ appPackage: 'com.lynx.explorer' });
const page = await lynx.newPage();
await page.goto('http://localhost:8080/main.lynx.bundle');

const result = await judgeAndroidAgent({
page,
task: 'The Lynx app should show a checkout confirmation screen.',
steps: ['Dismiss permission dialog if it appears.'],
});
```

When `dimension` is omitted, `judgePage` keeps the legacy
`visual-correctness` prompt. GEQI scoring can pass one of these dimensions:

Expand Down
37 changes: 37 additions & 0 deletions packages/genui/ui-judge/etc/ui-judge.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@

import type { Page } from '@playwright/test';

// Warning: (ae-missing-release-tag) "judgeAndroidAgent" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function judgeAndroidAgent(options: JudgeAndroidAgentOptions): Promise<UiJudgeResult>;

// Warning: (ae-missing-release-tag) "JudgeAndroidAgentOptions" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface JudgeAndroidAgentOptions {
// (undocumented)
dimension?: UiJudgeDimension;
// (undocumented)
page: KittenLynxJudgePage;
// (undocumented)
reference?: string;
// (undocumented)
steps?: string[];
// (undocumented)
task: string;
// (undocumented)
timeoutMs?: number;
}

// Warning: (ae-missing-release-tag) "judgePage" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
Expand All @@ -29,6 +52,20 @@ export interface JudgePageOptions {
timeoutMs?: number;
}

// Warning: (ae-missing-release-tag) "KittenLynxJudgePage" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface KittenLynxJudgePage {
// (undocumented)
screenshot(options?: {
format?: 'jpeg' | 'png' | 'webp';
path?: string;
quality?: number;
}): Promise<Buffer>;
// (undocumented)
url(): string;
}

// Warning: (ae-missing-release-tag) "UiJudgeDimension" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
Expand Down
9 changes: 7 additions & 2 deletions packages/genui/ui-judge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@
"scripts": {
"api-extractor": "node ../scripts/run-api-extractor.mjs",
"build": "rslib build",
"test": "pnpm --dir ../a2ui-catalog-extractor build && pnpm --dir ../a2ui build && playwright test"
"test": "pnpm run test:playwright",
"test:android": "vitest run --config vitest.config.ts",
"test:playwright": "playwright test"
},
"dependencies": {
"@lynx-js/genui": "workspace:*",
"@midscene/core": "^1.8.0",
"@midscene/web": "^1.8.0",
"@playwright/test": "^1.58.2"
},
"devDependencies": {
"@lynx-js/genui-a2ui": "workspace:*",
"@lynx-js/genui-a2ui-catalog-extractor": "workspace:*",
"@lynx-js/kitten-lynx-test-infra": "workspace:*",
"@microsoft/api-extractor": "catalog:",
"@types/node": "^24.10.13"
"@types/node": "^24.10.13",
"vitest": "^3.2.4"
},
"engines": {
"node": ">=22"
Expand Down
1 change: 1 addition & 0 deletions packages/genui/ui-judge/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { PlaywrightTestConfig } from '@playwright/test';

const config: PlaywrightTestConfig = defineConfig({
testDir: './tests',
testIgnore: '**/*.vitest.spec.ts',
timeout: 180_000,
fullyParallel: false,
workers: 1,
Expand Down
1 change: 1 addition & 0 deletions packages/genui/ui-judge/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const config: RslibConfig = defineConfig({
entry: {
index: './src/index.ts',
},
tsconfigPath: './tsconfig.build.json',
},
});

Expand Down
Loading
Loading