miaoda-game-devkit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -14
- package/bin/miaoda-game-lint.js +1 -1
- package/dist/cli/lint.js +1 -1
- package/dist/gameplay-audit-CYqLL8gY.d.mts +193 -0
- package/dist/gameplay-audit-CYqLL8gY.d.ts +193 -0
- package/dist/index.d.mts +47 -83
- package/dist/index.d.ts +47 -83
- package/dist/index.js +481 -93
- package/dist/index.mjs +480 -93
- package/dist/lint/contracts.config.mjs +36 -0
- package/dist/lint/gameplay-audit.contract.mjs +426 -0
- package/dist/lint/gameplay-contract.contract.mjs +1227 -0
- package/dist/lint/phaser-headless.contract.mjs +1575 -0
- package/dist/lint/phaser-text-assertions.contract.mjs +32 -0
- package/dist/lint/resource-import-plugin.contract.mjs +97 -0
- package/dist/lint/setup.mjs +31 -34
- package/dist/lint/vitest-config.contract.mjs +471 -0
- package/dist/vitest-config.d.mts +6 -2
- package/dist/vitest-config.d.ts +6 -2
- package/dist/vitest-config.js +344 -5
- package/dist/vitest-config.mjs +342 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
# miaoda-game-devkit
|
|
2
2
|
|
|
3
|
-
妙搭 Phaser
|
|
3
|
+
妙搭 Phaser 4 游戏模板的共享开发基础设施。
|
|
4
4
|
|
|
5
5
|
该开发工具包提供:
|
|
6
6
|
|
|
7
|
-
- 支持 DOM
|
|
7
|
+
- 支持 DOM 鼠标、键盘和触摸输入的确定性 Phaser 无头测试宿主;
|
|
8
8
|
- 不依赖渲染器的运行时、交互区域和文字健康检查;
|
|
9
9
|
- 由模板平台维护的通用 Vitest 契约测试;
|
|
10
10
|
- 统一的 `miaoda-game-lint` 命令,聚合 TypeScript、Biome、Tailwind、
|
|
11
11
|
Oxlint 和 Phaser 通用契约检查。
|
|
12
12
|
- 统一的 `defineGameVitestConfig` 配置工厂,固定 Phaser 无头测试依赖的基础配置,
|
|
13
|
-
|
|
13
|
+
同时允许每个游戏声明玩法审计清单,并配置额外 alias、setup 文件和超时。
|
|
14
14
|
|
|
15
|
-
`createHeadlessGame`
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
`createHeadlessGame` 还记录测试期间真实发生的输入、完整帧、物理步、Scene 注册与访问、
|
|
16
|
+
Scene 命令、restart、业务 checkpoint 和销毁。玩法测试可以调用
|
|
17
|
+
`host.assertGameplayEvidence()` 把这些客观事实设为门禁;它不推断业务语义,
|
|
18
|
+
authoritative state 仍必须由测试显式断言。
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
玩法测试应优先使用 `gameplayTest`。它在 Vitest 收集阶段写入静态 contract metadata,
|
|
21
|
+
在执行阶段绑定 HEADLESS host,并在 `onTestFinished` 校验 Ledger。`gameplayAudit` reporter
|
|
22
|
+
会在测试执行前报告未声明或重复的 contract,在测试结束后汇总缺失、失败、证据不足、
|
|
23
|
+
Scene 未访问和 registry 不一致。`registerGameplayContract` 保留为不参加执行前预检的低层接口。
|
|
22
24
|
|
|
23
25
|
模板直接调用开发工具包提供的命令行程序:
|
|
24
26
|
|
|
@@ -33,8 +35,8 @@
|
|
|
33
35
|
开发工具包自行维护工具版本,并从自身依赖中解析可执行文件,使用方不需要重复声明
|
|
34
36
|
Biome、Oxlint 或 tsgo。仓库中保留稳定的命令入口,因此 `dist/` 尚未生成时,pnpm
|
|
35
37
|
也能正确建立命令链接;工作区开发状态下由 Nx 在模板检查前构建开发工具包,发布包则
|
|
36
|
-
直接携带运行时 `dist
|
|
37
|
-
|
|
38
|
+
直接携带运行时 `dist/`。开发工具包内部的 `*.test.*` 测试不会进入 npm 包;随包运行的
|
|
39
|
+
通用契约测试以 `*.contract.mjs` 形式发布。
|
|
38
40
|
|
|
39
41
|
monorepo 中的模板可以放置只包含 `extends: ["miaoda-game-devkit/biome"]` 的薄配置,
|
|
40
42
|
用于让 Biome 从模板 package.json 判断依赖声明。规则本身仍由本开发工具包维护;生成后的
|
|
@@ -49,11 +51,39 @@ monorepo 中的模板可以放置只包含 `extends: ["miaoda-game-devkit/biome"
|
|
|
49
51
|
同步帧逻辑使用 `stepFrames()`;`update()` 返回 Promise 时使用
|
|
50
52
|
`await stepFramesAsync()`,其他同步操作启动生命周期 Promise 后使用 `await settle()`。
|
|
51
53
|
|
|
54
|
+
输入驱动会把 DOM 兼容事件发送到 Phaser 实际配置的输入目标,并在每次事件后检查异步
|
|
55
|
+
错误和运行时健康状态。坐标参数使用 Phaser 游戏坐标:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
host.stepFrames();
|
|
59
|
+
await host.input.mouse.click(100, 80);
|
|
60
|
+
|
|
61
|
+
await host.input.keyboard.down(Phaser.Input.Keyboard.KeyCodes.RIGHT);
|
|
62
|
+
host.stepFrames(10);
|
|
63
|
+
await host.input.keyboard.up(Phaser.Input.Keyboard.KeyCodes.RIGHT);
|
|
64
|
+
|
|
65
|
+
await host.input.touch.start(40, 40);
|
|
66
|
+
await host.input.touch.move(100, 80);
|
|
67
|
+
await host.input.touch.end(100, 80);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`click`、`press` 和 `tap` 适合离散命令。需要验证按住、拖动或滑动时,应分别调用开始和
|
|
71
|
+
结束方法,并在两者之间推进完整 Phaser 帧。JSDOM 没有布局和真实渲染器;这些驱动验证
|
|
72
|
+
DOM 到 Phaser 的输入、命中测试和游戏状态变化,不验证 Canvas/WebGL 像素、CSS 布局、
|
|
73
|
+
浏览器焦点或真实触摸策略。
|
|
74
|
+
|
|
52
75
|
模板的 `vitest.config.ts` 应从 `miaoda-game-devkit/vitest-config` 导入
|
|
53
76
|
`defineGameVitestConfig`。JSDOM 环境、Phaser 浏览器构建别名、通用 setup 和 mock 隔离
|
|
54
|
-
以及 Scene coverage
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
以及 Scene coverage 均由开发工具包统一维护。模板必须传入 `projectRoot` 和
|
|
78
|
+
`gameplayAudit`;清单列出生产 Scene、关键 contract、负责的测试文件和证据要求。
|
|
79
|
+
确有需要时,可以通过 `aliases`、`additionalSetupFiles`、`testTimeout` 和 `hookTimeout`
|
|
80
|
+
增加安全的项目配置,不能覆盖测试范围、运行环境、审计 reporter 或 coverage 基线。
|
|
81
|
+
|
|
82
|
+
`gameplayTest` 会自动要求访问其声明的 Scene 并完成 host 销毁。玩法进展、恢复和终局应在
|
|
83
|
+
断言 authoritative state 后调用 `host.checkpoint("progress")`、
|
|
84
|
+
`host.checkpoint("recovery")` 等稳定检查点,并在清单中通过 `requireCheckpoint` 声明。
|
|
85
|
+
运行结束会输出稳定的 `GAMEPLAY_AUDIT: ALL CONTRACTS PASSED` 或
|
|
86
|
+
`GAMEPLAY_AUDIT: ... FAILED` 标识。
|
|
57
87
|
|
|
58
88
|
## 发布顺序
|
|
59
89
|
|
package/bin/miaoda-game-lint.js
CHANGED
package/dist/cli/lint.js
CHANGED
|
@@ -43,7 +43,7 @@ async function runContracts() {
|
|
|
43
43
|
"vitest",
|
|
44
44
|
"vitest",
|
|
45
45
|
"vitest",
|
|
46
|
-
["run", "--config", (0, import_node_path.join)(devkitRoot, "dist/lint/
|
|
46
|
+
["run", "--config", (0, import_node_path.join)(devkitRoot, "dist/lint/contracts.config.mjs")]
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
async function runAllChecks() {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as Phaser from 'phaser';
|
|
2
|
+
|
|
3
|
+
interface HeadlessGameHost<TScene extends Phaser.Scene> {
|
|
4
|
+
/** 当前隔离的 Phaser 游戏实例。 */
|
|
5
|
+
game: Phaser.Game;
|
|
6
|
+
/** 作为测试入口启动的 Scene 实例。 */
|
|
7
|
+
scene: TScene;
|
|
8
|
+
/** 通过生产 DOM 目标驱动 Phaser 的输入工具。 */
|
|
9
|
+
input: HeadlessInputDriver;
|
|
10
|
+
/** 当前隔离 host 实际执行过的客观操作证据。 */
|
|
11
|
+
readonly evidence: Readonly<HeadlessGameplayEvidence>;
|
|
12
|
+
/** 当缺少声明的客观操作证据时抛出错误。 */
|
|
13
|
+
assertGameplayEvidence(requirements?: HeadlessGameplayEvidenceRequirements): void;
|
|
14
|
+
/** 等待同步操作触发的异步生命周期和错误检查完成。 */
|
|
15
|
+
settle(): Promise<void>;
|
|
16
|
+
/** 推进指定数量的完整 Phaser 帧。 */
|
|
17
|
+
stepFrames(count?: number): void;
|
|
18
|
+
/** 推进完整 Phaser 帧并等待异步 update 或生命周期完成。 */
|
|
19
|
+
stepFramesAsync(count?: number): Promise<void>;
|
|
20
|
+
/** 在限定帧数内推进游戏,直到条件成立。 */
|
|
21
|
+
stepUntil(condition: () => boolean, maxFrames?: number): number;
|
|
22
|
+
/** 精确推进指定次数的 Arcade Physics 单步。 */
|
|
23
|
+
stepPhysics(steps?: number): void;
|
|
24
|
+
/** 记录由测试定义的可序列化玩法检查点。 */
|
|
25
|
+
checkpoint(id: string): void;
|
|
26
|
+
/** 立即检查入口 Scene 的文字健康状态。 */
|
|
27
|
+
assertTextHealth(): void;
|
|
28
|
+
/** 幂等销毁游戏并记录清理证据。 */
|
|
29
|
+
destroy(): void;
|
|
30
|
+
}
|
|
31
|
+
/** 单个 HEADLESS host 在测试期间记录的只读玩法 Ledger。 */
|
|
32
|
+
interface HeadlessGameplayEvidence {
|
|
33
|
+
/** 通过 host 公共 API 推进的完整 Phaser 帧数。 */
|
|
34
|
+
frames: number;
|
|
35
|
+
/** 显式执行的 Arcade Physics 单步数。 */
|
|
36
|
+
physicsSteps: number;
|
|
37
|
+
/** 投递到 Phaser canvas 的 DOM 鼠标事件数。 */
|
|
38
|
+
mouseEvents: number;
|
|
39
|
+
/** 投递到 Phaser 配置目标的 DOM 键盘事件数。 */
|
|
40
|
+
keyboardEvents: number;
|
|
41
|
+
/** 投递到 Phaser 配置目标的 DOM 触摸事件数。 */
|
|
42
|
+
touchEvents: number;
|
|
43
|
+
/** 通过输入辅助方法投递的复合 click 次数。 */
|
|
44
|
+
clicks: number;
|
|
45
|
+
/** 当前 host 注册过的全部 Scene key。 */
|
|
46
|
+
registeredScenes: string[];
|
|
47
|
+
/** 至少进入过 Active 或 Paused 状态的 Scene key。 */
|
|
48
|
+
visitedScenes: string[];
|
|
49
|
+
/** 通过 Scene Plugin 的 restart 命令重启过的 Scene key。 */
|
|
50
|
+
restartedScenes: string[];
|
|
51
|
+
/** 测试显式记录的玩法检查点。 */
|
|
52
|
+
checkpoints: string[];
|
|
53
|
+
/** host 是否完成了游戏销毁流程。 */
|
|
54
|
+
destroyed: boolean;
|
|
55
|
+
/** 通过 Scene Plugin 公共 API 观察到的 Scene 操作。 */
|
|
56
|
+
transitions: ReadonlyArray<{
|
|
57
|
+
from: string;
|
|
58
|
+
to: string;
|
|
59
|
+
method: "start" | "launch" | "switch" | "sleep" | "wake";
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
/** 一个玩法契约可以要求 host 必须留下的客观证据。 */
|
|
63
|
+
interface HeadlessGameplayEvidenceRequirements {
|
|
64
|
+
/** 至少要求一次真实 DOM 鼠标、键盘或触摸事件。 */
|
|
65
|
+
requireInput?: boolean;
|
|
66
|
+
/** 至少要求推进一个完整 Phaser 帧。 */
|
|
67
|
+
requireFrameAdvance?: boolean;
|
|
68
|
+
/** 至少要求执行一次显式 Arcade Physics 单步。 */
|
|
69
|
+
requirePhysicsStep?: boolean;
|
|
70
|
+
/** 要求发生一个或多个目标 key 匹配的 Scene 转场。 */
|
|
71
|
+
requireTransition?: string | string[];
|
|
72
|
+
/** 要求一个或多个 Scene 至少进入过 Active 或 Paused 状态。 */
|
|
73
|
+
requireScene?: string | string[];
|
|
74
|
+
/** 要求一个或多个 Scene 通过生产 restart 命令重启。 */
|
|
75
|
+
requireRestart?: string | string[];
|
|
76
|
+
/** 要求记录一个或多个由玩法测试定义的检查点。 */
|
|
77
|
+
requireCheckpoint?: string | string[];
|
|
78
|
+
/** 要求测试清理阶段完成 host 销毁。 */
|
|
79
|
+
requireDestroy?: boolean;
|
|
80
|
+
}
|
|
81
|
+
/** 用于把游戏坐标转换为 DOM 坐标的合成显示边界。 */
|
|
82
|
+
interface HeadlessCanvasBounds {
|
|
83
|
+
left: number;
|
|
84
|
+
top: number;
|
|
85
|
+
width: number;
|
|
86
|
+
height: number;
|
|
87
|
+
}
|
|
88
|
+
/** 由 Phaser 游戏坐标转换得到的 DOM 客户区坐标。 */
|
|
89
|
+
interface HeadlessInputPoint {
|
|
90
|
+
clientX: number;
|
|
91
|
+
clientY: number;
|
|
92
|
+
}
|
|
93
|
+
/** 通过 Phaser 已配置鼠标目标投递事件的 DOM 鼠标控制器。 */
|
|
94
|
+
interface HeadlessMouseInput {
|
|
95
|
+
/** 移动鼠标指针,并可指定当前按键位掩码。 */
|
|
96
|
+
move(x: number, y: number, buttons?: number): Promise<void>;
|
|
97
|
+
/** 按下鼠标主键并保持按下状态。 */
|
|
98
|
+
down(x: number, y: number): Promise<void>;
|
|
99
|
+
/** 释放鼠标主键。 */
|
|
100
|
+
up(x: number, y: number): Promise<void>;
|
|
101
|
+
/** 连续投递移动、按下和释放事件,完成一次离散点击。 */
|
|
102
|
+
click(x: number, y: number): Promise<void>;
|
|
103
|
+
}
|
|
104
|
+
/** 附加到 Phaser 数字键码上的可选 DOM KeyboardEvent 字段。 */
|
|
105
|
+
interface HeadlessKeyboardEventOptions {
|
|
106
|
+
key?: string;
|
|
107
|
+
code?: string;
|
|
108
|
+
repeat?: boolean;
|
|
109
|
+
altKey?: boolean;
|
|
110
|
+
ctrlKey?: boolean;
|
|
111
|
+
metaKey?: boolean;
|
|
112
|
+
shiftKey?: boolean;
|
|
113
|
+
}
|
|
114
|
+
/** 通过 Phaser 已配置键盘目标投递事件的 DOM 键盘控制器。 */
|
|
115
|
+
interface HeadlessKeyboardInput {
|
|
116
|
+
/** 按下按键并保持,直到调用匹配的 up。 */
|
|
117
|
+
down(keyCode: number, options?: HeadlessKeyboardEventOptions): Promise<void>;
|
|
118
|
+
/** 释放之前保持按下的按键。 */
|
|
119
|
+
up(keyCode: number, options?: HeadlessKeyboardEventOptions): Promise<void>;
|
|
120
|
+
/** 不推进 Phaser 帧,连续投递一次离散的按下和释放。 */
|
|
121
|
+
press(keyCode: number, options?: HeadlessKeyboardEventOptions): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
/** 通过 Phaser 已配置触摸目标投递兼容 DOM 事件的触摸控制器。 */
|
|
124
|
+
interface HeadlessTouchInput {
|
|
125
|
+
/** 使用在整个手势中保持稳定的标识开始一次触摸。 */
|
|
126
|
+
start(x: number, y: number, identifier?: number): Promise<void>;
|
|
127
|
+
/** 移动一次仍处于活动状态的触摸。 */
|
|
128
|
+
move(x: number, y: number, identifier?: number): Promise<void>;
|
|
129
|
+
/** 正常结束一次活动触摸。 */
|
|
130
|
+
end(x: number, y: number, identifier?: number): Promise<void>;
|
|
131
|
+
/** 取消一次活动触摸。 */
|
|
132
|
+
cancel(x: number, y: number, identifier?: number): Promise<void>;
|
|
133
|
+
/** 连续投递触摸开始和结束,完成一次离散轻触。 */
|
|
134
|
+
tap(x: number, y: number, identifier?: number): Promise<void>;
|
|
135
|
+
}
|
|
136
|
+
/** 确定性 DOM 到 Phaser 测试使用的公开输入边界。 */
|
|
137
|
+
interface HeadlessInputDriver {
|
|
138
|
+
/** Phaser HEADLESS 为输入系统创建的 Canvas。 */
|
|
139
|
+
readonly canvas: HTMLCanvasElement;
|
|
140
|
+
/** DOM 鼠标事件驱动器。 */
|
|
141
|
+
readonly mouse: HeadlessMouseInput;
|
|
142
|
+
/** DOM 键盘事件驱动器。 */
|
|
143
|
+
readonly keyboard: HeadlessKeyboardInput;
|
|
144
|
+
/** DOM 触摸事件驱动器。 */
|
|
145
|
+
readonly touch: HeadlessTouchInput;
|
|
146
|
+
/** 设置用于坐标换算和 JSDOM 命中模拟的 Canvas 显示边界。 */
|
|
147
|
+
setCanvasBounds(bounds: HeadlessCanvasBounds): void;
|
|
148
|
+
/** 把 Phaser 游戏坐标换算为 DOM 客户区坐标。 */
|
|
149
|
+
gameToClient(x: number, y: number): HeadlessInputPoint;
|
|
150
|
+
/** 等待输入回调触发的异步任务并重新执行运行时健康检查。 */
|
|
151
|
+
settle(): Promise<void>;
|
|
152
|
+
}
|
|
153
|
+
/** 创建 HEADLESS 游戏时允许覆盖的 Phaser 配置和测试专用选项。 */
|
|
154
|
+
type HeadlessGameOptions = Omit<Phaser.Types.Core.GameConfig, "type" | "scene" | "callbacks"> & {
|
|
155
|
+
/** 等待 Scene 完成创建的最长时间。 */
|
|
156
|
+
bootTimeoutMs?: number;
|
|
157
|
+
/** 初始 Scene 启动前必须注册的其他 Scene 类型。 */
|
|
158
|
+
additionalScenes?: Phaser.Types.Scenes.SceneType[];
|
|
159
|
+
};
|
|
160
|
+
/** 创建隔离的 Phaser 游戏,并在入口 Scene 完成 create 后返回。 */
|
|
161
|
+
declare function createHeadlessGame<TScene extends Phaser.Scene>(scene: TScene, options?: HeadlessGameOptions): Promise<HeadlessGameHost<TScene>>;
|
|
162
|
+
|
|
163
|
+
/** 一个必须由指定测试文件证明的玩法契约。 */
|
|
164
|
+
interface GameplayAuditContract extends HeadlessGameplayEvidenceRequirements {
|
|
165
|
+
/** 在整个游戏项目中唯一且稳定的契约标识。 */
|
|
166
|
+
id: string;
|
|
167
|
+
/** 相对于游戏项目根目录的 Vitest 测试文件。 */
|
|
168
|
+
testFile: string;
|
|
169
|
+
/** 该契约负责证明的生产 Scene key。 */
|
|
170
|
+
scenes: string[];
|
|
171
|
+
}
|
|
172
|
+
/** 游戏项目在一次 Vitest 运行中必须完成的玩法审计清单。 */
|
|
173
|
+
interface GameplayAuditOptions {
|
|
174
|
+
/** 生产 Scene registry 中预期出现的全部 Scene key。 */
|
|
175
|
+
scenes: string[];
|
|
176
|
+
/** 必须定义、执行并满足证据要求的玩法契约。 */
|
|
177
|
+
contracts: GameplayAuditContract[];
|
|
178
|
+
}
|
|
179
|
+
/** 通过 Vitest task metadata 在 worker 与 reporter 之间传递的契约数据。 */
|
|
180
|
+
interface GameplayContractMetadata {
|
|
181
|
+
/** 契约唯一标识。 */
|
|
182
|
+
id: string;
|
|
183
|
+
/** 契约负责证明的生产 Scene key。 */
|
|
184
|
+
scenes: string[];
|
|
185
|
+
/** 已规范化的客观证据要求。 */
|
|
186
|
+
requirements: HeadlessGameplayEvidenceRequirements;
|
|
187
|
+
/** 测试完成后从 HEADLESS host 复制出的可序列化证据。 */
|
|
188
|
+
evidence?: HeadlessGameplayEvidence;
|
|
189
|
+
/** worker 是否已完成单测试证据校验。 */
|
|
190
|
+
verified?: boolean;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { type GameplayContractMetadata as G, type HeadlessGameHost as H, type HeadlessGameplayEvidenceRequirements as a, type GameplayAuditContract as b, type GameplayAuditOptions as c, type HeadlessCanvasBounds as d, type HeadlessGameOptions as e, type HeadlessGameplayEvidence as f, type HeadlessInputDriver as g, type HeadlessInputPoint as h, type HeadlessKeyboardEventOptions as i, type HeadlessKeyboardInput as j, type HeadlessMouseInput as k, type HeadlessTouchInput as l, createHeadlessGame as m };
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as Phaser from 'phaser';
|
|
2
|
+
|
|
3
|
+
interface HeadlessGameHost<TScene extends Phaser.Scene> {
|
|
4
|
+
/** 当前隔离的 Phaser 游戏实例。 */
|
|
5
|
+
game: Phaser.Game;
|
|
6
|
+
/** 作为测试入口启动的 Scene 实例。 */
|
|
7
|
+
scene: TScene;
|
|
8
|
+
/** 通过生产 DOM 目标驱动 Phaser 的输入工具。 */
|
|
9
|
+
input: HeadlessInputDriver;
|
|
10
|
+
/** 当前隔离 host 实际执行过的客观操作证据。 */
|
|
11
|
+
readonly evidence: Readonly<HeadlessGameplayEvidence>;
|
|
12
|
+
/** 当缺少声明的客观操作证据时抛出错误。 */
|
|
13
|
+
assertGameplayEvidence(requirements?: HeadlessGameplayEvidenceRequirements): void;
|
|
14
|
+
/** 等待同步操作触发的异步生命周期和错误检查完成。 */
|
|
15
|
+
settle(): Promise<void>;
|
|
16
|
+
/** 推进指定数量的完整 Phaser 帧。 */
|
|
17
|
+
stepFrames(count?: number): void;
|
|
18
|
+
/** 推进完整 Phaser 帧并等待异步 update 或生命周期完成。 */
|
|
19
|
+
stepFramesAsync(count?: number): Promise<void>;
|
|
20
|
+
/** 在限定帧数内推进游戏,直到条件成立。 */
|
|
21
|
+
stepUntil(condition: () => boolean, maxFrames?: number): number;
|
|
22
|
+
/** 精确推进指定次数的 Arcade Physics 单步。 */
|
|
23
|
+
stepPhysics(steps?: number): void;
|
|
24
|
+
/** 记录由测试定义的可序列化玩法检查点。 */
|
|
25
|
+
checkpoint(id: string): void;
|
|
26
|
+
/** 立即检查入口 Scene 的文字健康状态。 */
|
|
27
|
+
assertTextHealth(): void;
|
|
28
|
+
/** 幂等销毁游戏并记录清理证据。 */
|
|
29
|
+
destroy(): void;
|
|
30
|
+
}
|
|
31
|
+
/** 单个 HEADLESS host 在测试期间记录的只读玩法 Ledger。 */
|
|
32
|
+
interface HeadlessGameplayEvidence {
|
|
33
|
+
/** 通过 host 公共 API 推进的完整 Phaser 帧数。 */
|
|
34
|
+
frames: number;
|
|
35
|
+
/** 显式执行的 Arcade Physics 单步数。 */
|
|
36
|
+
physicsSteps: number;
|
|
37
|
+
/** 投递到 Phaser canvas 的 DOM 鼠标事件数。 */
|
|
38
|
+
mouseEvents: number;
|
|
39
|
+
/** 投递到 Phaser 配置目标的 DOM 键盘事件数。 */
|
|
40
|
+
keyboardEvents: number;
|
|
41
|
+
/** 投递到 Phaser 配置目标的 DOM 触摸事件数。 */
|
|
42
|
+
touchEvents: number;
|
|
43
|
+
/** 通过输入辅助方法投递的复合 click 次数。 */
|
|
44
|
+
clicks: number;
|
|
45
|
+
/** 当前 host 注册过的全部 Scene key。 */
|
|
46
|
+
registeredScenes: string[];
|
|
47
|
+
/** 至少进入过 Active 或 Paused 状态的 Scene key。 */
|
|
48
|
+
visitedScenes: string[];
|
|
49
|
+
/** 通过 Scene Plugin 的 restart 命令重启过的 Scene key。 */
|
|
50
|
+
restartedScenes: string[];
|
|
51
|
+
/** 测试显式记录的玩法检查点。 */
|
|
52
|
+
checkpoints: string[];
|
|
53
|
+
/** host 是否完成了游戏销毁流程。 */
|
|
54
|
+
destroyed: boolean;
|
|
55
|
+
/** 通过 Scene Plugin 公共 API 观察到的 Scene 操作。 */
|
|
56
|
+
transitions: ReadonlyArray<{
|
|
57
|
+
from: string;
|
|
58
|
+
to: string;
|
|
59
|
+
method: "start" | "launch" | "switch" | "sleep" | "wake";
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
/** 一个玩法契约可以要求 host 必须留下的客观证据。 */
|
|
63
|
+
interface HeadlessGameplayEvidenceRequirements {
|
|
64
|
+
/** 至少要求一次真实 DOM 鼠标、键盘或触摸事件。 */
|
|
65
|
+
requireInput?: boolean;
|
|
66
|
+
/** 至少要求推进一个完整 Phaser 帧。 */
|
|
67
|
+
requireFrameAdvance?: boolean;
|
|
68
|
+
/** 至少要求执行一次显式 Arcade Physics 单步。 */
|
|
69
|
+
requirePhysicsStep?: boolean;
|
|
70
|
+
/** 要求发生一个或多个目标 key 匹配的 Scene 转场。 */
|
|
71
|
+
requireTransition?: string | string[];
|
|
72
|
+
/** 要求一个或多个 Scene 至少进入过 Active 或 Paused 状态。 */
|
|
73
|
+
requireScene?: string | string[];
|
|
74
|
+
/** 要求一个或多个 Scene 通过生产 restart 命令重启。 */
|
|
75
|
+
requireRestart?: string | string[];
|
|
76
|
+
/** 要求记录一个或多个由玩法测试定义的检查点。 */
|
|
77
|
+
requireCheckpoint?: string | string[];
|
|
78
|
+
/** 要求测试清理阶段完成 host 销毁。 */
|
|
79
|
+
requireDestroy?: boolean;
|
|
80
|
+
}
|
|
81
|
+
/** 用于把游戏坐标转换为 DOM 坐标的合成显示边界。 */
|
|
82
|
+
interface HeadlessCanvasBounds {
|
|
83
|
+
left: number;
|
|
84
|
+
top: number;
|
|
85
|
+
width: number;
|
|
86
|
+
height: number;
|
|
87
|
+
}
|
|
88
|
+
/** 由 Phaser 游戏坐标转换得到的 DOM 客户区坐标。 */
|
|
89
|
+
interface HeadlessInputPoint {
|
|
90
|
+
clientX: number;
|
|
91
|
+
clientY: number;
|
|
92
|
+
}
|
|
93
|
+
/** 通过 Phaser 已配置鼠标目标投递事件的 DOM 鼠标控制器。 */
|
|
94
|
+
interface HeadlessMouseInput {
|
|
95
|
+
/** 移动鼠标指针,并可指定当前按键位掩码。 */
|
|
96
|
+
move(x: number, y: number, buttons?: number): Promise<void>;
|
|
97
|
+
/** 按下鼠标主键并保持按下状态。 */
|
|
98
|
+
down(x: number, y: number): Promise<void>;
|
|
99
|
+
/** 释放鼠标主键。 */
|
|
100
|
+
up(x: number, y: number): Promise<void>;
|
|
101
|
+
/** 连续投递移动、按下和释放事件,完成一次离散点击。 */
|
|
102
|
+
click(x: number, y: number): Promise<void>;
|
|
103
|
+
}
|
|
104
|
+
/** 附加到 Phaser 数字键码上的可选 DOM KeyboardEvent 字段。 */
|
|
105
|
+
interface HeadlessKeyboardEventOptions {
|
|
106
|
+
key?: string;
|
|
107
|
+
code?: string;
|
|
108
|
+
repeat?: boolean;
|
|
109
|
+
altKey?: boolean;
|
|
110
|
+
ctrlKey?: boolean;
|
|
111
|
+
metaKey?: boolean;
|
|
112
|
+
shiftKey?: boolean;
|
|
113
|
+
}
|
|
114
|
+
/** 通过 Phaser 已配置键盘目标投递事件的 DOM 键盘控制器。 */
|
|
115
|
+
interface HeadlessKeyboardInput {
|
|
116
|
+
/** 按下按键并保持,直到调用匹配的 up。 */
|
|
117
|
+
down(keyCode: number, options?: HeadlessKeyboardEventOptions): Promise<void>;
|
|
118
|
+
/** 释放之前保持按下的按键。 */
|
|
119
|
+
up(keyCode: number, options?: HeadlessKeyboardEventOptions): Promise<void>;
|
|
120
|
+
/** 不推进 Phaser 帧,连续投递一次离散的按下和释放。 */
|
|
121
|
+
press(keyCode: number, options?: HeadlessKeyboardEventOptions): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
/** 通过 Phaser 已配置触摸目标投递兼容 DOM 事件的触摸控制器。 */
|
|
124
|
+
interface HeadlessTouchInput {
|
|
125
|
+
/** 使用在整个手势中保持稳定的标识开始一次触摸。 */
|
|
126
|
+
start(x: number, y: number, identifier?: number): Promise<void>;
|
|
127
|
+
/** 移动一次仍处于活动状态的触摸。 */
|
|
128
|
+
move(x: number, y: number, identifier?: number): Promise<void>;
|
|
129
|
+
/** 正常结束一次活动触摸。 */
|
|
130
|
+
end(x: number, y: number, identifier?: number): Promise<void>;
|
|
131
|
+
/** 取消一次活动触摸。 */
|
|
132
|
+
cancel(x: number, y: number, identifier?: number): Promise<void>;
|
|
133
|
+
/** 连续投递触摸开始和结束,完成一次离散轻触。 */
|
|
134
|
+
tap(x: number, y: number, identifier?: number): Promise<void>;
|
|
135
|
+
}
|
|
136
|
+
/** 确定性 DOM 到 Phaser 测试使用的公开输入边界。 */
|
|
137
|
+
interface HeadlessInputDriver {
|
|
138
|
+
/** Phaser HEADLESS 为输入系统创建的 Canvas。 */
|
|
139
|
+
readonly canvas: HTMLCanvasElement;
|
|
140
|
+
/** DOM 鼠标事件驱动器。 */
|
|
141
|
+
readonly mouse: HeadlessMouseInput;
|
|
142
|
+
/** DOM 键盘事件驱动器。 */
|
|
143
|
+
readonly keyboard: HeadlessKeyboardInput;
|
|
144
|
+
/** DOM 触摸事件驱动器。 */
|
|
145
|
+
readonly touch: HeadlessTouchInput;
|
|
146
|
+
/** 设置用于坐标换算和 JSDOM 命中模拟的 Canvas 显示边界。 */
|
|
147
|
+
setCanvasBounds(bounds: HeadlessCanvasBounds): void;
|
|
148
|
+
/** 把 Phaser 游戏坐标换算为 DOM 客户区坐标。 */
|
|
149
|
+
gameToClient(x: number, y: number): HeadlessInputPoint;
|
|
150
|
+
/** 等待输入回调触发的异步任务并重新执行运行时健康检查。 */
|
|
151
|
+
settle(): Promise<void>;
|
|
152
|
+
}
|
|
153
|
+
/** 创建 HEADLESS 游戏时允许覆盖的 Phaser 配置和测试专用选项。 */
|
|
154
|
+
type HeadlessGameOptions = Omit<Phaser.Types.Core.GameConfig, "type" | "scene" | "callbacks"> & {
|
|
155
|
+
/** 等待 Scene 完成创建的最长时间。 */
|
|
156
|
+
bootTimeoutMs?: number;
|
|
157
|
+
/** 初始 Scene 启动前必须注册的其他 Scene 类型。 */
|
|
158
|
+
additionalScenes?: Phaser.Types.Scenes.SceneType[];
|
|
159
|
+
};
|
|
160
|
+
/** 创建隔离的 Phaser 游戏,并在入口 Scene 完成 create 后返回。 */
|
|
161
|
+
declare function createHeadlessGame<TScene extends Phaser.Scene>(scene: TScene, options?: HeadlessGameOptions): Promise<HeadlessGameHost<TScene>>;
|
|
162
|
+
|
|
163
|
+
/** 一个必须由指定测试文件证明的玩法契约。 */
|
|
164
|
+
interface GameplayAuditContract extends HeadlessGameplayEvidenceRequirements {
|
|
165
|
+
/** 在整个游戏项目中唯一且稳定的契约标识。 */
|
|
166
|
+
id: string;
|
|
167
|
+
/** 相对于游戏项目根目录的 Vitest 测试文件。 */
|
|
168
|
+
testFile: string;
|
|
169
|
+
/** 该契约负责证明的生产 Scene key。 */
|
|
170
|
+
scenes: string[];
|
|
171
|
+
}
|
|
172
|
+
/** 游戏项目在一次 Vitest 运行中必须完成的玩法审计清单。 */
|
|
173
|
+
interface GameplayAuditOptions {
|
|
174
|
+
/** 生产 Scene registry 中预期出现的全部 Scene key。 */
|
|
175
|
+
scenes: string[];
|
|
176
|
+
/** 必须定义、执行并满足证据要求的玩法契约。 */
|
|
177
|
+
contracts: GameplayAuditContract[];
|
|
178
|
+
}
|
|
179
|
+
/** 通过 Vitest task metadata 在 worker 与 reporter 之间传递的契约数据。 */
|
|
180
|
+
interface GameplayContractMetadata {
|
|
181
|
+
/** 契约唯一标识。 */
|
|
182
|
+
id: string;
|
|
183
|
+
/** 契约负责证明的生产 Scene key。 */
|
|
184
|
+
scenes: string[];
|
|
185
|
+
/** 已规范化的客观证据要求。 */
|
|
186
|
+
requirements: HeadlessGameplayEvidenceRequirements;
|
|
187
|
+
/** 测试完成后从 HEADLESS host 复制出的可序列化证据。 */
|
|
188
|
+
evidence?: HeadlessGameplayEvidence;
|
|
189
|
+
/** worker 是否已完成单测试证据校验。 */
|
|
190
|
+
verified?: boolean;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { type GameplayContractMetadata as G, type HeadlessGameHost as H, type HeadlessGameplayEvidenceRequirements as a, type GameplayAuditContract as b, type GameplayAuditOptions as c, type HeadlessCanvasBounds as d, type HeadlessGameOptions as e, type HeadlessGameplayEvidence as f, type HeadlessInputDriver as g, type HeadlessInputPoint as h, type HeadlessKeyboardEventOptions as i, type HeadlessKeyboardInput as j, type HeadlessMouseInput as k, type HeadlessTouchInput as l, createHeadlessGame as m };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,111 +1,75 @@
|
|
|
1
|
+
import { G as GameplayContractMetadata, H as HeadlessGameHost, a as HeadlessGameplayEvidenceRequirements } from './gameplay-audit-CYqLL8gY.mjs';
|
|
2
|
+
export { b as GameplayAuditContract, c as GameplayAuditOptions, d as HeadlessCanvasBounds, e as HeadlessGameOptions, f as HeadlessGameplayEvidence, g as HeadlessInputDriver, h as HeadlessInputPoint, i as HeadlessKeyboardEventOptions, j as HeadlessKeyboardInput, k as HeadlessMouseInput, l as HeadlessTouchInput, m as createHeadlessGame } from './gameplay-audit-CYqLL8gY.mjs';
|
|
1
3
|
import * as Phaser from 'phaser';
|
|
4
|
+
import { TestContext, TestOptions } from 'vitest';
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
game: Phaser.Game;
|
|
5
|
-
scene: TScene;
|
|
6
|
-
input: HeadlessInputDriver;
|
|
7
|
-
/** 当前隔离 host 实际执行过的客观操作证据。 */
|
|
8
|
-
readonly evidence: Readonly<HeadlessGameplayEvidence>;
|
|
9
|
-
/** 当缺少所要求的输入、时间、物理或转场证据时失败。 */
|
|
10
|
-
assertGameplayEvidence(requirements?: HeadlessGameplayEvidenceRequirements): void;
|
|
11
|
-
settle(): Promise<void>;
|
|
12
|
-
stepFrames(count?: number): void;
|
|
13
|
-
stepFramesAsync(count?: number): Promise<void>;
|
|
14
|
-
stepUntil(condition: () => boolean, maxFrames?: number): number;
|
|
15
|
-
stepPhysics(steps?: number): void;
|
|
16
|
-
assertTextHealth(): void;
|
|
17
|
-
destroy(): void;
|
|
18
|
-
}
|
|
19
|
-
interface HeadlessGameplayEvidence {
|
|
20
|
-
/** 通过 host 公共 API 推进的完整 Phaser 帧数。 */
|
|
21
|
-
frames: number;
|
|
22
|
-
/** 显式执行的 Arcade Physics 单步数。 */
|
|
23
|
-
physicsSteps: number;
|
|
24
|
-
/** 投递到 Phaser canvas 的 DOM 鼠标事件数。 */
|
|
25
|
-
mouseEvents: number;
|
|
26
|
-
/** 通过输入辅助方法投递的复合 click 次数。 */
|
|
27
|
-
clicks: number;
|
|
28
|
-
/** 通过 Scene Plugin 公共 API 观察到的 Scene 操作。 */
|
|
29
|
-
transitions: ReadonlyArray<{
|
|
30
|
-
from: string;
|
|
31
|
-
to: string;
|
|
32
|
-
method: "start" | "launch" | "switch" | "sleep" | "wake";
|
|
33
|
-
}>;
|
|
34
|
-
}
|
|
35
|
-
interface HeadlessGameplayEvidenceRequirements {
|
|
36
|
-
/** 至少要求一次真实 DOM 鼠标事件。 */
|
|
37
|
-
requireInput?: boolean;
|
|
38
|
-
/** 至少要求推进一个完整 Phaser 帧。 */
|
|
39
|
-
requireFrameAdvance?: boolean;
|
|
40
|
-
/** 至少要求执行一次显式 Arcade Physics 单步。 */
|
|
41
|
-
requirePhysicsStep?: boolean;
|
|
42
|
-
/** 要求发生一个或多个目标 key 匹配的 Scene 转场。 */
|
|
43
|
-
requireTransition?: string | string[];
|
|
44
|
-
}
|
|
45
|
-
interface HeadlessCanvasBounds {
|
|
46
|
-
left: number;
|
|
47
|
-
top: number;
|
|
48
|
-
width: number;
|
|
49
|
-
height: number;
|
|
50
|
-
}
|
|
51
|
-
interface HeadlessInputPoint {
|
|
52
|
-
clientX: number;
|
|
53
|
-
clientY: number;
|
|
54
|
-
}
|
|
55
|
-
interface HeadlessMouseInput {
|
|
56
|
-
move(x: number, y: number, buttons?: number): Promise<void>;
|
|
57
|
-
down(x: number, y: number): Promise<void>;
|
|
58
|
-
up(x: number, y: number): Promise<void>;
|
|
59
|
-
click(x: number, y: number): Promise<void>;
|
|
60
|
-
}
|
|
61
|
-
interface HeadlessInputDriver {
|
|
62
|
-
readonly canvas: HTMLCanvasElement;
|
|
63
|
-
readonly mouse: HeadlessMouseInput;
|
|
64
|
-
setCanvasBounds(bounds: HeadlessCanvasBounds): void;
|
|
65
|
-
gameToClient(x: number, y: number): HeadlessInputPoint;
|
|
66
|
-
settle(): Promise<void>;
|
|
67
|
-
}
|
|
68
|
-
type HeadlessGameOptions = Omit<Phaser.Types.Core.GameConfig, "type" | "scene" | "callbacks"> & {
|
|
69
|
-
/** Maximum time to wait for Scene creation to complete. */
|
|
70
|
-
bootTimeoutMs?: number;
|
|
71
|
-
/** Scene types that must be registered before the initial Scene starts. */
|
|
72
|
-
additionalScenes?: Phaser.Types.Scenes.SceneType[];
|
|
73
|
-
};
|
|
74
|
-
/** Creates one isolated Phaser game and resolves after the scene has run create(). */
|
|
75
|
-
declare function createHeadlessGame<TScene extends Phaser.Scene>(scene: TScene, options?: HeadlessGameOptions): Promise<HeadlessGameHost<TScene>>;
|
|
76
|
-
|
|
6
|
+
/** 将单个 Vitest 测试绑定到玩法 Ledger 的契约选项。 */
|
|
77
7
|
interface GameplayContractOptions extends HeadlessGameplayEvidenceRequirements {
|
|
78
|
-
/**
|
|
8
|
+
/** 生产玩法清单中稳定且唯一的 contract 标识。 */
|
|
79
9
|
id: string;
|
|
10
|
+
/** 当前契约负责证明的生产 Scene key。 */
|
|
11
|
+
scenes?: string[];
|
|
12
|
+
}
|
|
13
|
+
/** 在收集阶段声明静态 metadata 的 gameplayTest 选项。 */
|
|
14
|
+
interface GameplayTestOptions extends GameplayContractOptions {
|
|
15
|
+
/** 当前契约必须负责至少一个生产 Scene。 */
|
|
16
|
+
scenes: string[];
|
|
17
|
+
/** 透传给 Vitest 的非 metadata 测试选项。 */
|
|
18
|
+
testOptions?: Omit<TestOptions, "meta">;
|
|
80
19
|
}
|
|
20
|
+
/** 单个玩法测试用于绑定 host 和主动校验证据的句柄。 */
|
|
81
21
|
interface GameplayContractHandle<TScene extends Phaser.Scene> {
|
|
82
22
|
/** 将当前 contract 绑定到实际创建的 HEADLESS host。 */
|
|
83
23
|
attachHost(host: HeadlessGameHost<TScene>): void;
|
|
84
|
-
/**
|
|
24
|
+
/** 立即验证证据;通常由 onTestFinished 自动执行。 */
|
|
85
25
|
verify(): void;
|
|
86
26
|
}
|
|
27
|
+
/** registerGameplayContract 所需的最小 Vitest 测试上下文。 */
|
|
87
28
|
interface GameplayTestContext {
|
|
29
|
+
/** 当前 Vitest task 及其可序列化 metadata。 */
|
|
88
30
|
task: {
|
|
89
|
-
meta:
|
|
31
|
+
meta: {
|
|
32
|
+
gameplayContract?: GameplayContractMetadata;
|
|
33
|
+
};
|
|
90
34
|
};
|
|
35
|
+
/** 注册在测试及 afterEach 结束后执行的回调。 */
|
|
91
36
|
onTestFinished(callback: () => void | Promise<void>): void;
|
|
37
|
+
/** 注册仅在当前测试失败时执行的回调。 */
|
|
92
38
|
onTestFailed?: (callback: () => void) => void;
|
|
93
39
|
}
|
|
40
|
+
/** gameplayTest 测试函数接收的契约参数。 */
|
|
41
|
+
interface GameplayTestArguments<TScene extends Phaser.Scene> {
|
|
42
|
+
/** Vitest 原生测试上下文。 */
|
|
43
|
+
context: TestContext;
|
|
44
|
+
/** 已自动注册到当前 task 的玩法契约句柄。 */
|
|
45
|
+
contract: GameplayContractHandle<TScene>;
|
|
46
|
+
}
|
|
94
47
|
/**
|
|
95
48
|
* 将一个 gameplay contract 绑定到当前 Vitest 测试。
|
|
96
49
|
*
|
|
97
|
-
* 证据校验在 `onTestFinished` 中执行,晚于 `afterEach
|
|
98
|
-
*
|
|
99
|
-
* contract 元数据只包含可序列化值,供 reporter 在 worker 之外汇总。
|
|
50
|
+
* 证据校验在 `onTestFinished` 中执行,晚于 `afterEach`,因此通用 cleanup
|
|
51
|
+
* 可以先销毁 host。写入 metadata 的内容全部可序列化,供主线程 reporter 汇总。
|
|
100
52
|
*/
|
|
101
53
|
declare function registerGameplayContract<TScene extends Phaser.Scene>(context: GameplayTestContext, options: GameplayContractOptions): GameplayContractHandle<TScene>;
|
|
54
|
+
/**
|
|
55
|
+
* 定义一个可被全局 reporter 在执行前发现的玩法测试。
|
|
56
|
+
*
|
|
57
|
+
* 该方法在测试收集阶段写入静态 metadata,并在执行阶段自动注册同一个 contract。
|
|
58
|
+
*/
|
|
59
|
+
declare function gameplayTest<TScene extends Phaser.Scene>(name: string, options: GameplayTestOptions, run: (arguments_: GameplayTestArguments<TScene>) => void | Promise<void>): void;
|
|
60
|
+
declare module "vitest" {
|
|
61
|
+
interface TaskMeta {
|
|
62
|
+
/** 供玩法审计 reporter 读取的可序列化契约数据。 */
|
|
63
|
+
gameplayContract?: GameplayContractMetadata;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
102
66
|
|
|
103
67
|
declare function assertSceneRuntimeHealth(scene: Phaser.Scene): void;
|
|
104
68
|
declare function assertSceneInteractiveHealth(scene: Phaser.Scene): void;
|
|
105
69
|
|
|
106
|
-
/**
|
|
70
|
+
/** 返回 Bitmap Font 中缺失的非空白 UTF-16 code unit。 */
|
|
107
71
|
declare function collectMissingBitmapGlyphs(text: string, chars: Record<number, unknown>): string[];
|
|
108
|
-
/**
|
|
72
|
+
/** 在文字状态损坏时抛出适合 CI 和编码智能体处理的错误。 */
|
|
109
73
|
declare function assertSceneTextHealth(scene: Phaser.Scene): void;
|
|
110
74
|
|
|
111
|
-
export { type GameplayContractHandle,
|
|
75
|
+
export { type GameplayContractHandle, GameplayContractMetadata, type GameplayContractOptions, type GameplayTestArguments, type GameplayTestContext, type GameplayTestOptions, HeadlessGameHost, HeadlessGameplayEvidenceRequirements, assertSceneInteractiveHealth, assertSceneRuntimeHealth, assertSceneTextHealth, collectMissingBitmapGlyphs, gameplayTest, registerGameplayContract };
|