@theaiplatform/miniapp-sdk 0.3.0 → 0.3.2
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 +25 -5
- package/dist/rspack/index.d.ts +8 -0
- package/dist/rspack/index.js +22 -19
- package/dist/testing/rstest.d.ts +118 -0
- package/dist/testing/rstest.js +249 -0
- package/package.json +32 -5
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# The AI Platform Miniapp SDK
|
|
2
2
|
|
|
3
|
-
`@theaiplatform/miniapp-sdk` is the
|
|
4
|
-
|
|
3
|
+
`@theaiplatform/miniapp-sdk` is the TypeScript SDK for building, testing, and
|
|
4
|
+
shipping portable miniapps on The AI Platform.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
7
|
pnpm add @theaiplatform/miniapp-sdk
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
The package provides the host-injected SDK API, isolated surface lifecycle
|
|
11
|
-
types, browser helpers, portable React UI components, manifest schema,
|
|
12
|
-
Rspack/Rslib integration used to produce
|
|
13
|
-
targets.
|
|
11
|
+
types, browser helpers, portable React UI components, manifest schema,
|
|
12
|
+
Rstest/Playwright integration, and the Rspack/Rslib integration used to produce
|
|
13
|
+
descriptor-backed Module Federation targets.
|
|
14
14
|
|
|
15
15
|
Start with the [Miniapp SDK documentation](https://docs.theaiplatform.app/miniapps/).
|
|
16
16
|
|
|
@@ -27,8 +27,28 @@ Start with the [Miniapp SDK documentation](https://docs.theaiplatform.app/miniap
|
|
|
27
27
|
- `@theaiplatform/miniapp-sdk/surface`
|
|
28
28
|
- `@theaiplatform/miniapp-sdk/config`
|
|
29
29
|
- `@theaiplatform/miniapp-sdk/rspack`
|
|
30
|
+
- `@theaiplatform/miniapp-sdk/testing/rstest`
|
|
30
31
|
- `@theaiplatform/miniapp-sdk/config-schema.json`
|
|
31
32
|
|
|
33
|
+
## In-app E2E testing
|
|
34
|
+
|
|
35
|
+
The `/testing/rstest` entry point extends Rstest's Playwright fixtures with a
|
|
36
|
+
host-provided miniapp surface and exact TAP session provenance. Tests can assert
|
|
37
|
+
both the rendered UI and the package/surface identity selected by the Test Lab:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { expect, test } from '@theaiplatform/miniapp-sdk/testing/rstest';
|
|
41
|
+
|
|
42
|
+
test('mounts the miniapp surface', async ({ surface, tap }) => {
|
|
43
|
+
await expect(surface.locator('body')).toBeVisible();
|
|
44
|
+
expect(tap.packageId).toBe('tap_pkg_example_0001');
|
|
45
|
+
expect(tap.surfaceId).toBe('example-surface');
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Install `@rstest/core@0.11.3`, `@rstest/playwright@0.11.3`, and Playwright as
|
|
50
|
+
development dependencies when using this optional entry point.
|
|
51
|
+
|
|
32
52
|
The host API is injected at runtime. Importing an entry point is safe in build
|
|
33
53
|
tools and tests; host-backed calls fail when used outside a supported runtime.
|
|
34
54
|
`sdk.storage` provides revisioned non-secret JSON scoped by the host to the
|
package/dist/rspack/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Format } from 'ajv';
|
|
1
2
|
import type { LibConfig } from '@rslib/core';
|
|
2
3
|
import { ModuleFederationOptions } from '@module-federation/rsbuild-plugin';
|
|
3
4
|
import { RsbuildPlugin } from '@rsbuild/core';
|
|
@@ -29,8 +30,15 @@ declare type Config = {
|
|
|
29
30
|
|
|
30
31
|
export declare const pluginTap: (options?: Config) => RsbuildPlugin;
|
|
31
32
|
|
|
33
|
+
/** Registers the canonical JSON Schema formats used by TAP manifests. */
|
|
34
|
+
export declare const registerTapManifestAjvFormats: <Registry extends TapManifestAjvFormatRegistry>(ajv: Registry) => Registry;
|
|
35
|
+
|
|
32
36
|
export declare const tapLib: (options?: Config) => LibConfig;
|
|
33
37
|
|
|
38
|
+
declare interface TapManifestAjvFormatRegistry {
|
|
39
|
+
addFormat(name: string, format: Format): unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
/** Emitted-package scan used to reject machine-local build artifacts. */
|
|
35
43
|
export declare type TapPackageArtifactPortabilityOptions = {
|
|
36
44
|
output: string;
|
package/dist/rspack/index.js
CHANGED
|
@@ -1994,27 +1994,30 @@ const isUrl = (value)=>{
|
|
|
1994
1994
|
return false;
|
|
1995
1995
|
}
|
|
1996
1996
|
};
|
|
1997
|
+
const registerTapManifestAjvFormats = (ajv)=>{
|
|
1998
|
+
ajv.addFormat('uri', {
|
|
1999
|
+
type: 'string',
|
|
2000
|
+
validate: isUrl
|
|
2001
|
+
});
|
|
2002
|
+
ajv.addFormat('uint8', {
|
|
2003
|
+
type: 'number',
|
|
2004
|
+
validate: (value)=>Number.isInteger(value) && value >= 0 && value <= 255
|
|
2005
|
+
});
|
|
2006
|
+
ajv.addFormat('uint16', {
|
|
2007
|
+
type: 'number',
|
|
2008
|
+
validate: (value)=>Number.isInteger(value) && value >= 0 && value <= 65535
|
|
2009
|
+
});
|
|
2010
|
+
ajv.addFormat('uint64', {
|
|
2011
|
+
type: 'number',
|
|
2012
|
+
validate: (value)=>Number.isSafeInteger(value) && value >= 0
|
|
2013
|
+
});
|
|
2014
|
+
return ajv;
|
|
2015
|
+
};
|
|
1997
2016
|
const getSchemaValidator = async ()=>{
|
|
1998
2017
|
if (!schemaValidator) {
|
|
1999
|
-
const ajv = new __rspack_external_ajv_dist_2020_js_e85899db["default"]({
|
|
2018
|
+
const ajv = registerTapManifestAjvFormats(new __rspack_external_ajv_dist_2020_js_e85899db["default"]({
|
|
2000
2019
|
allErrors: true
|
|
2001
|
-
});
|
|
2002
|
-
ajv.addFormat('uri', {
|
|
2003
|
-
type: 'string',
|
|
2004
|
-
validate: isUrl
|
|
2005
|
-
});
|
|
2006
|
-
ajv.addFormat('uint8', {
|
|
2007
|
-
type: 'number',
|
|
2008
|
-
validate: (value)=>Number.isInteger(value) && value >= 0 && value <= 255
|
|
2009
|
-
});
|
|
2010
|
-
ajv.addFormat('uint16', {
|
|
2011
|
-
type: 'number',
|
|
2012
|
-
validate: (value)=>Number.isInteger(value) && value >= 0 && value <= 65535
|
|
2013
|
-
});
|
|
2014
|
-
ajv.addFormat('uint64', {
|
|
2015
|
-
type: 'number',
|
|
2016
|
-
validate: (value)=>Number.isSafeInteger(value) && value >= 0
|
|
2017
|
-
});
|
|
2020
|
+
}));
|
|
2018
2021
|
schemaValidator = ajv.compile(config_schema_namespaceObject);
|
|
2019
2022
|
}
|
|
2020
2023
|
return schemaValidator;
|
|
@@ -3512,4 +3515,4 @@ const tapLib = (options = {})=>{
|
|
|
3512
3515
|
]
|
|
3513
3516
|
};
|
|
3514
3517
|
};
|
|
3515
|
-
export { assembleTapPackage, assertPortableTapPackageArtifacts, pluginTap, tapLib };
|
|
3518
|
+
export { assembleTapPackage, assertPortableTapPackageArtifacts, pluginTap, registerTapManifestAjvFormats, tapLib };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { afterAll } from '@rstest/playwright';
|
|
2
|
+
import { beforeAll } from '@rstest/playwright';
|
|
3
|
+
import type { Browser } from 'playwright';
|
|
4
|
+
import type { BrowserContext } from 'playwright';
|
|
5
|
+
import { describe } from '@rstest/playwright';
|
|
6
|
+
import { expect } from '@rstest/playwright';
|
|
7
|
+
import type { FrameLocator } from 'playwright';
|
|
8
|
+
import type { Page } from 'playwright';
|
|
9
|
+
import type { PlaywrightFixtures } from '@rstest/playwright';
|
|
10
|
+
import type { PlaywrightTest } from '@rstest/playwright';
|
|
11
|
+
import type { TestContext } from '@rstest/core';
|
|
12
|
+
import type { TestOptions } from '@rstest/core';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
|
|
15
|
+
export { afterAll }
|
|
16
|
+
|
|
17
|
+
export declare const afterEach: TapAfterEachHook;
|
|
18
|
+
|
|
19
|
+
export { beforeAll }
|
|
20
|
+
|
|
21
|
+
export declare const beforeEach: TapBeforeEachHook;
|
|
22
|
+
|
|
23
|
+
export { describe }
|
|
24
|
+
|
|
25
|
+
export { expect }
|
|
26
|
+
|
|
27
|
+
declare type MergeContext<ExtraContext, FixturesContext> = {
|
|
28
|
+
[Key in keyof FixturesContext | keyof ExtraContext]: Key extends keyof FixturesContext ? FixturesContext[Key] : Key extends keyof ExtraContext ? ExtraContext[Key] : never;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** Validate a driver session before any endpoint or provenance field is used. */
|
|
32
|
+
export declare function parseTapMiniappTestSession(value: unknown): TapMiniappTestSession;
|
|
33
|
+
|
|
34
|
+
declare const sessionSchema: z.ZodObject<{
|
|
35
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
36
|
+
runId: z.ZodString;
|
|
37
|
+
workspaceId: z.ZodString;
|
|
38
|
+
channelId: z.ZodString;
|
|
39
|
+
packageId: z.ZodString;
|
|
40
|
+
installationId: z.ZodString;
|
|
41
|
+
mode: z.ZodEnum<{
|
|
42
|
+
surface: "surface";
|
|
43
|
+
"live-tap": "live-tap";
|
|
44
|
+
}>;
|
|
45
|
+
dataScope: z.ZodEnum<{
|
|
46
|
+
fixture: "fixture";
|
|
47
|
+
"selected-channel": "selected-channel";
|
|
48
|
+
}>;
|
|
49
|
+
permissionScenario: z.ZodString;
|
|
50
|
+
surfaceId: z.ZodString;
|
|
51
|
+
surfaceSelector: z.ZodString;
|
|
52
|
+
surfaceAssetOrigin: z.ZodString;
|
|
53
|
+
pageUrlPrefix: z.ZodString;
|
|
54
|
+
cdpEndpoint: z.ZodString;
|
|
55
|
+
cdpAuthorization: z.ZodString;
|
|
56
|
+
artifactDirectory: z.ZodString;
|
|
57
|
+
allowedNetworkOrigins: z.ZodArray<z.ZodString>;
|
|
58
|
+
credentialAliases: z.ZodArray<z.ZodString>;
|
|
59
|
+
releaseDigest: z.ZodOptional<z.ZodString>;
|
|
60
|
+
localGeneration: z.ZodOptional<z.ZodNumber>;
|
|
61
|
+
sourceDigest: z.ZodString;
|
|
62
|
+
testBundleDigest: z.ZodString;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
|
|
65
|
+
declare type TapAfterEachCallback<ExtraContext> = (context: TapHookContext<ExtraContext>) => void | Promise<void>;
|
|
66
|
+
|
|
67
|
+
declare type TapAfterEachHook<ExtraContext = TapRstestFixtures> = (callback: TapAfterEachCallback<ExtraContext>, timeout?: number) => void;
|
|
68
|
+
|
|
69
|
+
declare type TapBeforeEachCallback<ExtraContext> = (context: TapHookContext<ExtraContext>) => void | TapAfterEachCallback<ExtraContext> | Promise<void | TapAfterEachCallback<ExtraContext>>;
|
|
70
|
+
|
|
71
|
+
declare type TapBeforeEachHook<ExtraContext = TapRstestFixtures> = (callback: TapBeforeEachCallback<ExtraContext>, timeout?: number) => void;
|
|
72
|
+
|
|
73
|
+
declare type TapHookContext<ExtraContext> = TestContext & ExtraContext;
|
|
74
|
+
|
|
75
|
+
export declare type TapMiniappTestControl = {
|
|
76
|
+
/** Restore the run's declared fixture and permission scenario. */
|
|
77
|
+
reset: () => Promise<void>;
|
|
78
|
+
/** Ask the TAP host to emit one versioned platform event through the normal frame bridge. */
|
|
79
|
+
emitHostEvent: (event: string, payload?: unknown) => Promise<void>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export declare type TapMiniappTestFixture = Readonly<Pick<TapMiniappTestSession, 'channelId' | 'dataScope' | 'installationId' | 'localGeneration' | 'mode' | 'packageId' | 'permissionScenario' | 'releaseDigest' | 'runId' | 'sourceDigest' | 'surfaceId' | 'surfaceAssetOrigin' | 'testBundleDigest' | 'workspaceId'> & {
|
|
83
|
+
readonly allowedNetworkOrigins: readonly string[];
|
|
84
|
+
readonly credentialAliases: readonly string[];
|
|
85
|
+
control: TapMiniappTestControl;
|
|
86
|
+
}>;
|
|
87
|
+
|
|
88
|
+
export declare type TapMiniappTestSession = z.infer<typeof sessionSchema>;
|
|
89
|
+
|
|
90
|
+
export declare type TapRstestFixtures = {
|
|
91
|
+
browser: Browser;
|
|
92
|
+
context: BrowserContext;
|
|
93
|
+
page: Page;
|
|
94
|
+
/** Locator for the authorized miniapp iframe mounted by TAP's real frame host. */
|
|
95
|
+
surface: FrameLocator;
|
|
96
|
+
/** Provenance and scoped host controls for this exact test run. */
|
|
97
|
+
tap: TapMiniappTestFixture;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
declare type TapRstestTest<ExtraContext = TapRstestFixtures> = Omit<PlaywrightTest<ExtraContext>, 'afterEach' | 'beforeEach' | 'extend'> & {
|
|
101
|
+
(description: string, callback?: (context: TapHookContext<ExtraContext>) => void | Promise<void>, timeout?: number): void;
|
|
102
|
+
(description: string, options: TestOptions, callback?: (context: TapHookContext<ExtraContext>) => void | Promise<void>): void;
|
|
103
|
+
afterEach: TapAfterEachHook<ExtraContext>;
|
|
104
|
+
beforeEach: TapBeforeEachHook<ExtraContext>;
|
|
105
|
+
extend: <FixturesContext extends Record<string, unknown> = Record<never, never>>(fixtures: PlaywrightFixtures<FixturesContext, ExtraContext>) => TapRstestTest<MergeContext<ExtraContext, FixturesContext>>;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** Rstest's per-test context plus TAP's authorized browser and run fixtures. */
|
|
109
|
+
export declare type TapTestContext = TestContext & TapRstestFixtures;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Rstest's Playwright test API with TAP-owned browser, page, iframe, and run
|
|
113
|
+
* fixtures. The upstream runner still owns scheduling, assertions, hooks, and
|
|
114
|
+
* reporting; TAP owns the privileged browser/session boundary.
|
|
115
|
+
*/
|
|
116
|
+
export declare const test: TapRstestTest;
|
|
117
|
+
|
|
118
|
+
export { }
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import * as __rspack_external__rstest_playwright_5734ff29 from "@rstest/playwright";
|
|
2
|
+
import * as __rspack_external_node_fs_promises_3b710708 from "node:fs/promises";
|
|
3
|
+
import * as __rspack_external_node_path_806ed179 from "node:path";
|
|
4
|
+
import * as __rspack_external_playwright from "playwright";
|
|
5
|
+
import * as __rspack_external_zod from "zod";
|
|
6
|
+
const SESSION_ENV = 'TAP_MINIAPP_TEST_SESSION_FILE';
|
|
7
|
+
const sessionSchema = __rspack_external_zod.z.object({
|
|
8
|
+
schemaVersion: __rspack_external_zod.z.literal(1),
|
|
9
|
+
runId: __rspack_external_zod.z.string().min(1),
|
|
10
|
+
workspaceId: __rspack_external_zod.z.string().min(1),
|
|
11
|
+
channelId: __rspack_external_zod.z.string().min(1),
|
|
12
|
+
packageId: __rspack_external_zod.z.string().min(1),
|
|
13
|
+
installationId: __rspack_external_zod.z.string().min(1),
|
|
14
|
+
mode: __rspack_external_zod.z["enum"]([
|
|
15
|
+
'surface',
|
|
16
|
+
'live-tap'
|
|
17
|
+
]),
|
|
18
|
+
dataScope: __rspack_external_zod.z["enum"]([
|
|
19
|
+
'fixture',
|
|
20
|
+
'selected-channel'
|
|
21
|
+
]),
|
|
22
|
+
permissionScenario: __rspack_external_zod.z.string().min(1),
|
|
23
|
+
surfaceId: __rspack_external_zod.z.string().min(1),
|
|
24
|
+
surfaceSelector: __rspack_external_zod.z.string().min(1),
|
|
25
|
+
surfaceAssetOrigin: __rspack_external_zod.z.string().url(),
|
|
26
|
+
pageUrlPrefix: __rspack_external_zod.z.string().url(),
|
|
27
|
+
cdpEndpoint: __rspack_external_zod.z.string().url(),
|
|
28
|
+
cdpAuthorization: __rspack_external_zod.z.string().min(1),
|
|
29
|
+
artifactDirectory: __rspack_external_zod.z.string().min(1),
|
|
30
|
+
allowedNetworkOrigins: __rspack_external_zod.z.array(__rspack_external_zod.z.string().url()).max(128),
|
|
31
|
+
credentialAliases: __rspack_external_zod.z.array(__rspack_external_zod.z.string().min(1).max(256)).max(64),
|
|
32
|
+
releaseDigest: __rspack_external_zod.z.string().min(1).optional(),
|
|
33
|
+
localGeneration: __rspack_external_zod.z.number().int().nonnegative().optional(),
|
|
34
|
+
sourceDigest: __rspack_external_zod.z.string().min(1),
|
|
35
|
+
testBundleDigest: __rspack_external_zod.z.string().min(1)
|
|
36
|
+
});
|
|
37
|
+
let sessionPromise;
|
|
38
|
+
let browserPromise;
|
|
39
|
+
function parseTapMiniappTestSession(value) {
|
|
40
|
+
return sessionSchema.parse(value);
|
|
41
|
+
}
|
|
42
|
+
async function loadSession() {
|
|
43
|
+
sessionPromise ??= (async ()=>{
|
|
44
|
+
const sessionPath = process.env[SESSION_ENV]?.trim();
|
|
45
|
+
if (!sessionPath) throw new Error(`${SESSION_ENV} is missing. Start this suite from The AI Platform Miniapp Test Lab.`);
|
|
46
|
+
const raw = await (0, __rspack_external_node_fs_promises_3b710708.readFile)(sessionPath, 'utf8');
|
|
47
|
+
return parseTapMiniappTestSession(JSON.parse(raw));
|
|
48
|
+
})();
|
|
49
|
+
return sessionPromise;
|
|
50
|
+
}
|
|
51
|
+
function selectPage(context, session) {
|
|
52
|
+
const candidates = context.pages().filter((candidate)=>candidate.url().startsWith(session.pageUrlPrefix));
|
|
53
|
+
if (1 !== candidates.length) throw new Error(`TAP Browser Driver exposed ${candidates.length} matching pages for run ${session.runId}; expected exactly one.`);
|
|
54
|
+
const page = candidates[0];
|
|
55
|
+
if (!page) throw new Error('TAP Browser Driver page selection failed');
|
|
56
|
+
return page;
|
|
57
|
+
}
|
|
58
|
+
async function emitHostEvent(page, session, event, payload) {
|
|
59
|
+
if (!event.startsWith('tap.') || event.length > 160 || event.trim() !== event) throw new Error('TAP host events must be bounded, trimmed tap.* event names.');
|
|
60
|
+
await page.evaluate(({ controlEvent, event, payload, runId })=>{
|
|
61
|
+
const response = {};
|
|
62
|
+
globalThis.dispatchEvent(new CustomEvent(controlEvent, {
|
|
63
|
+
detail: {
|
|
64
|
+
event,
|
|
65
|
+
payload,
|
|
66
|
+
response,
|
|
67
|
+
runId
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
if (response.error) throw new Error(response.error);
|
|
71
|
+
if (!response.delivered) throw new Error(`The TAP frame host has no active subscriber for ${event}. Declare the event on the miniapp surface before using it in a test.`);
|
|
72
|
+
}, {
|
|
73
|
+
controlEvent: 'tap:miniapp-test-control',
|
|
74
|
+
event,
|
|
75
|
+
payload,
|
|
76
|
+
runId: session.runId
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function fixtureMetadata(session, page) {
|
|
80
|
+
return Object.freeze({
|
|
81
|
+
allowedNetworkOrigins: Object.freeze([
|
|
82
|
+
...session.allowedNetworkOrigins
|
|
83
|
+
]),
|
|
84
|
+
channelId: session.channelId,
|
|
85
|
+
credentialAliases: Object.freeze([
|
|
86
|
+
...session.credentialAliases
|
|
87
|
+
]),
|
|
88
|
+
control: Object.freeze({
|
|
89
|
+
emitHostEvent: (event, payload)=>emitHostEvent(page, session, event, payload),
|
|
90
|
+
reset: async ()=>{
|
|
91
|
+
await page.reload({
|
|
92
|
+
waitUntil: 'domcontentloaded'
|
|
93
|
+
});
|
|
94
|
+
await page.locator(session.surfaceSelector).waitFor({
|
|
95
|
+
state: 'attached'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}),
|
|
99
|
+
dataScope: session.dataScope,
|
|
100
|
+
installationId: session.installationId,
|
|
101
|
+
localGeneration: session.localGeneration,
|
|
102
|
+
mode: session.mode,
|
|
103
|
+
packageId: session.packageId,
|
|
104
|
+
permissionScenario: session.permissionScenario,
|
|
105
|
+
releaseDigest: session.releaseDigest,
|
|
106
|
+
runId: session.runId,
|
|
107
|
+
sourceDigest: session.sourceDigest,
|
|
108
|
+
surfaceAssetOrigin: session.surfaceAssetOrigin,
|
|
109
|
+
surfaceId: session.surfaceId,
|
|
110
|
+
testBundleDigest: session.testBundleDigest,
|
|
111
|
+
workspaceId: session.workspaceId
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function safeArtifactSegment(value) {
|
|
115
|
+
return value.replaceAll(/[^a-zA-Z0-9._-]/g, '-').slice(0, 160);
|
|
116
|
+
}
|
|
117
|
+
const test = __rspack_external__rstest_playwright_5734ff29.test.extend({
|
|
118
|
+
browser: async (_context, use)=>{
|
|
119
|
+
const session = await loadSession();
|
|
120
|
+
browserPromise ??= __rspack_external_playwright.chromium.connectOverCDP(session.cdpEndpoint, {
|
|
121
|
+
headers: {
|
|
122
|
+
authorization: `Bearer ${session.cdpAuthorization}`,
|
|
123
|
+
'x-tap-miniapp-test-run': session.runId
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
await use(await browserPromise);
|
|
127
|
+
},
|
|
128
|
+
context: async ({ browser, task }, use)=>{
|
|
129
|
+
const session = await loadSession();
|
|
130
|
+
const contexts = browser.contexts().filter((candidate)=>candidate.pages().some((page)=>page.url().startsWith(session.pageUrlPrefix)));
|
|
131
|
+
if (1 !== contexts.length) throw new Error(`TAP Browser Driver exposed ${contexts.length} matching contexts for run ${session.runId}; expected exactly one.`);
|
|
132
|
+
const context = contexts[0];
|
|
133
|
+
if (!context) throw new Error('TAP Browser Driver context selection failed');
|
|
134
|
+
const hostPage = selectPage(context, session);
|
|
135
|
+
const runtimeOrigins = new Set(hostPage.frames().map((frame)=>originOf(frame.url())).filter((origin)=>void 0 !== origin));
|
|
136
|
+
const allowedOrigins = new Set([
|
|
137
|
+
...runtimeOrigins,
|
|
138
|
+
new URL(session.surfaceAssetOrigin).origin,
|
|
139
|
+
...session.allowedNetworkOrigins.map((value)=>new URL(value).origin)
|
|
140
|
+
]);
|
|
141
|
+
const enforceNetworkPolicy = async (route)=>{
|
|
142
|
+
const url = route.request().url();
|
|
143
|
+
const origin = originOf(url);
|
|
144
|
+
if (void 0 === origin || allowedOrigins.has(origin)) return void await route.continue();
|
|
145
|
+
await route.abort('blockedbyclient');
|
|
146
|
+
};
|
|
147
|
+
await context.route('**/*', enforceNetworkPolicy);
|
|
148
|
+
const traceDirectory = (0, __rspack_external_node_path_806ed179.join)(session.artifactDirectory, 'traces', safeArtifactSegment(task.id));
|
|
149
|
+
await (0, __rspack_external_node_fs_promises_3b710708.mkdir)(traceDirectory, {
|
|
150
|
+
recursive: true
|
|
151
|
+
});
|
|
152
|
+
await context.tracing.start({
|
|
153
|
+
screenshots: true,
|
|
154
|
+
snapshots: true,
|
|
155
|
+
sources: true
|
|
156
|
+
});
|
|
157
|
+
try {
|
|
158
|
+
await use(context);
|
|
159
|
+
} finally{
|
|
160
|
+
await context.tracing.stop({
|
|
161
|
+
path: (0, __rspack_external_node_path_806ed179.join)(traceDirectory, 'trace.zip')
|
|
162
|
+
});
|
|
163
|
+
const traceSummary = {
|
|
164
|
+
schemaVersion: 2,
|
|
165
|
+
runId: session.runId,
|
|
166
|
+
taskId: task.id,
|
|
167
|
+
mode: session.mode,
|
|
168
|
+
dataScope: session.dataScope,
|
|
169
|
+
packageId: session.packageId,
|
|
170
|
+
surfaceId: session.surfaceId,
|
|
171
|
+
sourceDigest: session.sourceDigest,
|
|
172
|
+
testBundleDigest: session.testBundleDigest,
|
|
173
|
+
tracePath: 'trace.zip',
|
|
174
|
+
traceRedacted: false,
|
|
175
|
+
traceSensitivity: 'raw-sensitive',
|
|
176
|
+
traceAccessScope: 'channel-only',
|
|
177
|
+
traceMayContain: [
|
|
178
|
+
'authorization headers',
|
|
179
|
+
'cookies',
|
|
180
|
+
'credential values',
|
|
181
|
+
'network request and response bodies',
|
|
182
|
+
'DOM snapshots',
|
|
183
|
+
'screenshots',
|
|
184
|
+
'test sources'
|
|
185
|
+
]
|
|
186
|
+
};
|
|
187
|
+
await Promise.all([
|
|
188
|
+
(0, __rspack_external_node_fs_promises_3b710708.writeFile)((0, __rspack_external_node_path_806ed179.join)(traceDirectory, 'trace-summary.json'), `${JSON.stringify(traceSummary, void 0, 2)}\n`, 'utf8'),
|
|
189
|
+
(0, __rspack_external_node_fs_promises_3b710708.writeFile)((0, __rspack_external_node_path_806ed179.join)(traceDirectory, 'debug.md'), [
|
|
190
|
+
'# Miniapp test trace',
|
|
191
|
+
'',
|
|
192
|
+
`- Run: \`${session.runId}\``,
|
|
193
|
+
`- Rstest task: \`${task.id}\``,
|
|
194
|
+
`- Mode: \`${session.mode}\``,
|
|
195
|
+
`- Data scope: \`${session.dataScope}\``,
|
|
196
|
+
`- Surface: \`${session.surfaceId}\``,
|
|
197
|
+
`- Source digest: \`${session.sourceDigest}\``,
|
|
198
|
+
`- Test bundle digest: \`${session.testBundleDigest}\``,
|
|
199
|
+
'',
|
|
200
|
+
'`trace.zip` is raw, sensitive, channel-only evidence. It is not redacted and may contain authorization headers, cookies, credential values, network bodies, DOM snapshots, screenshots, and test source.',
|
|
201
|
+
'',
|
|
202
|
+
'Open the trace from The AI Platform Test Lab for debugging. Do not publish it or attach it to specialist context. The final assertion outcome is authoritative in `report.json` and `report.md`.',
|
|
203
|
+
''
|
|
204
|
+
].join('\n'), 'utf8')
|
|
205
|
+
]);
|
|
206
|
+
await context.unroute('**/*', enforceNetworkPolicy);
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
page: async ({ context }, use)=>{
|
|
210
|
+
const session = await loadSession();
|
|
211
|
+
const page = selectPage(context, session);
|
|
212
|
+
await page.locator(session.surfaceSelector).waitFor({
|
|
213
|
+
state: 'attached'
|
|
214
|
+
});
|
|
215
|
+
await use(page);
|
|
216
|
+
},
|
|
217
|
+
surface: async ({ page }, use)=>{
|
|
218
|
+
const session = await loadSession();
|
|
219
|
+
await use(page.frameLocator(session.surfaceSelector));
|
|
220
|
+
},
|
|
221
|
+
tap: async ({ page }, use)=>{
|
|
222
|
+
await use(fixtureMetadata(await loadSession(), page));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
const afterEach = __rspack_external__rstest_playwright_5734ff29.afterEach;
|
|
226
|
+
const beforeEach = __rspack_external__rstest_playwright_5734ff29.beforeEach;
|
|
227
|
+
function originOf(value) {
|
|
228
|
+
try {
|
|
229
|
+
const url = new URL(value);
|
|
230
|
+
if (matchesNonNetworkScheme(url.protocol)) return;
|
|
231
|
+
return url.origin;
|
|
232
|
+
} catch {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function matchesNonNetworkScheme(protocol) {
|
|
237
|
+
return NON_NETWORK_PROTOCOLS.has(protocol);
|
|
238
|
+
}
|
|
239
|
+
const NON_NETWORK_PROTOCOLS = new Set([
|
|
240
|
+
'about:',
|
|
241
|
+
'blob:',
|
|
242
|
+
'data:',
|
|
243
|
+
'file:'
|
|
244
|
+
]);
|
|
245
|
+
var afterAll = __rspack_external__rstest_playwright_5734ff29.afterAll;
|
|
246
|
+
var beforeAll = __rspack_external__rstest_playwright_5734ff29.beforeAll;
|
|
247
|
+
var describe = __rspack_external__rstest_playwright_5734ff29.describe;
|
|
248
|
+
var expect = __rspack_external__rstest_playwright_5734ff29.expect;
|
|
249
|
+
export { afterAll, afterEach, beforeAll, beforeEach, describe, expect, parseTapMiniappTestSession, test };
|
package/package.json
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theaiplatform/miniapp-sdk",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "TypeScript SDK for building, testing, and shipping portable miniapps on The AI Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
10
|
"miniapp",
|
|
11
|
+
"playwright",
|
|
12
|
+
"react",
|
|
13
|
+
"rstest",
|
|
14
|
+
"tap",
|
|
15
|
+
"typescript",
|
|
11
16
|
"module-federation",
|
|
12
17
|
"rspack",
|
|
13
18
|
"the-ai-platform"
|
|
@@ -54,7 +59,11 @@
|
|
|
54
59
|
"types": "./dist/rspack/index.d.ts",
|
|
55
60
|
"import": "./dist/rspack/index.js"
|
|
56
61
|
},
|
|
57
|
-
"./config-schema.json": "./config-schema.json"
|
|
62
|
+
"./config-schema.json": "./config-schema.json",
|
|
63
|
+
"./testing/rstest": {
|
|
64
|
+
"types": "./dist/testing/rstest.d.ts",
|
|
65
|
+
"import": "./dist/testing/rstest.js"
|
|
66
|
+
}
|
|
58
67
|
},
|
|
59
68
|
"main": "./dist/index.js",
|
|
60
69
|
"types": "./dist/index.d.ts",
|
|
@@ -119,7 +128,11 @@
|
|
|
119
128
|
"types": "./dist/rspack/index.d.ts",
|
|
120
129
|
"import": "./dist/rspack/index.js"
|
|
121
130
|
},
|
|
122
|
-
"./config-schema.json": "./config-schema.json"
|
|
131
|
+
"./config-schema.json": "./config-schema.json",
|
|
132
|
+
"./testing/rstest": {
|
|
133
|
+
"types": "./dist/testing/rstest.d.ts",
|
|
134
|
+
"import": "./dist/testing/rstest.js"
|
|
135
|
+
}
|
|
123
136
|
}
|
|
124
137
|
},
|
|
125
138
|
"peerDependencies": {
|
|
@@ -127,10 +140,13 @@
|
|
|
127
140
|
"@module-federation/runtime-tools": "2.8.0",
|
|
128
141
|
"@rsbuild/core": ">=2 <3",
|
|
129
142
|
"@rslib/core": ">=0.21 <1",
|
|
143
|
+
"@rstest/core": "0.11.3",
|
|
144
|
+
"@rstest/playwright": "0.11.3",
|
|
130
145
|
"@tanstack/react-query": ">=5 <6",
|
|
131
146
|
"@tanstack/react-router": ">=1 <2",
|
|
132
147
|
"react": ">=19 <20",
|
|
133
|
-
"react-dom": ">=19 <20"
|
|
148
|
+
"react-dom": ">=19 <20",
|
|
149
|
+
"playwright": ">=1.61 <2"
|
|
134
150
|
},
|
|
135
151
|
"peerDependenciesMeta": {
|
|
136
152
|
"@module-federation/rsbuild-plugin": {
|
|
@@ -145,6 +161,12 @@
|
|
|
145
161
|
"@rslib/core": {
|
|
146
162
|
"optional": true
|
|
147
163
|
},
|
|
164
|
+
"@rstest/core": {
|
|
165
|
+
"optional": true
|
|
166
|
+
},
|
|
167
|
+
"@rstest/playwright": {
|
|
168
|
+
"optional": true
|
|
169
|
+
},
|
|
148
170
|
"@tanstack/react-query": {
|
|
149
171
|
"optional": true
|
|
150
172
|
},
|
|
@@ -156,6 +178,9 @@
|
|
|
156
178
|
},
|
|
157
179
|
"react-dom": {
|
|
158
180
|
"optional": true
|
|
181
|
+
},
|
|
182
|
+
"playwright": {
|
|
183
|
+
"optional": true
|
|
159
184
|
}
|
|
160
185
|
},
|
|
161
186
|
"devDependencies": {
|
|
@@ -165,6 +190,7 @@
|
|
|
165
190
|
"@rsbuild/core": "^2.1.4",
|
|
166
191
|
"@rslib/core": "^0.23.2",
|
|
167
192
|
"@rstest/core": "catalog:rstest",
|
|
193
|
+
"@rstest/playwright": "catalog:rstest",
|
|
168
194
|
"@tailwindcss/postcss": "^4.3.0",
|
|
169
195
|
"@types/jsdom": "^28.0.3",
|
|
170
196
|
"@types/node": "catalog:node",
|
|
@@ -172,6 +198,7 @@
|
|
|
172
198
|
"@types/react-dom": "catalog:react",
|
|
173
199
|
"jsdom": "^29.1.1",
|
|
174
200
|
"postcss": "^8.5.6",
|
|
201
|
+
"playwright": "^1.61.0",
|
|
175
202
|
"tailwindcss": "^4.3.0",
|
|
176
203
|
"tw-animate-css": "^1.4.0",
|
|
177
204
|
"typescript": "catalog:typescript"
|