diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 781ac21c07..e103d8532c 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -196,10 +196,6 @@ "types": "./src/device-shutdown-runtime.ts", "default": "./src/device-shutdown-runtime.ts" }, - "./dispatch-disclosure-fixtures": { - "types": "./src/dispatch-disclosure.fixtures.ts", - "default": "./src/dispatch-disclosure.fixtures.ts" - }, "./divergence": { "types": "./src/facades/divergence.ts", "default": "./src/facades/divergence.ts" diff --git a/packages/daemon/src/execution/__tests__/scroll-movement.test.ts b/packages/daemon/src/execution/__tests__/scroll-movement.test.ts index 2b11a2de39..2e0395c21a 100644 --- a/packages/daemon/src/execution/__tests__/scroll-movement.test.ts +++ b/packages/daemon/src/execution/__tests__/scroll-movement.test.ts @@ -5,7 +5,9 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; + IOS_SIMULATOR, + MACOS_DEVICE, +} from '@agent-device/test-kit'; import type { SnapshotResult } from '@agent-device/contracts/interactor-types'; import { buildSnapshotState } from '@agent-device/capture-kit/snapshot-state'; import { AppError } from '@agent-device/kernel/errors'; @@ -13,7 +15,6 @@ import type { CommandFlags } from '@agent-device/contracts/command'; import type { Rect, SnapshotNode } from '@agent-device/kernel/snapshot'; import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { readScrollEdgeState } from '@agent-device/capture-kit/scroll-edge-state'; -import { IOS_SIMULATOR, MACOS_DEVICE } from '@agent-device/test-kit'; import { makeSession } from '../../__tests__/support/session-factories.ts'; import { expireRefFrame } from '../../core/ref-frame.ts'; import { diff --git a/packages/daemon/src/execution/interaction/internal/interaction-touch-direct-ios.test.ts b/packages/daemon/src/execution/interaction/internal/interaction-touch-direct-ios.test.ts index 659329ae34..1d949ab246 100644 --- a/packages/daemon/src/execution/interaction/internal/interaction-touch-direct-ios.test.ts +++ b/packages/daemon/src/execution/interaction/internal/interaction-touch-direct-ios.test.ts @@ -5,7 +5,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { beforeEach, expect, test } from 'vitest'; import { makeIosSession } from '../../../__tests__/support/session-factories.ts'; import { makeSessionStore } from '../../../__tests__/support/store-factory.ts'; diff --git a/packages/kernel/src/errors-dispatch-disclosure.test.ts b/packages/kernel/src/errors-dispatch-disclosure.test.ts new file mode 100644 index 0000000000..af252c4229 --- /dev/null +++ b/packages/kernel/src/errors-dispatch-disclosure.test.ts @@ -0,0 +1,13 @@ +import assert from 'node:assert/strict'; +import { test } from 'vitest'; +import { AppError, discloseDispatch, discloseUnclassifiedDispatch } from './errors.ts'; + +test('a producer verdict overwrites; the seam verdict fills only an unclassified failure', () => { + const error = new AppError('COMMAND_FAILED', 'tap failed', { hint: 'retry' }); + assert.equal(discloseUnclassifiedDispatch(error, 'unknown'), error); + assert.equal(error.details?.dispatched, 'unknown'); + assert.equal(discloseDispatch(error, 'no', { dispatchedSteps: 2 }), error); + assert.deepEqual(error.details, { hint: 'retry', dispatchedSteps: 2, dispatched: 'no' }); + discloseUnclassifiedDispatch(error, 'unknown'); + assert.equal(error.details?.dispatched, 'no'); +}); diff --git a/packages/platform-android/package.json b/packages/platform-android/package.json index 408fef48cc..944b8a454e 100644 --- a/packages/platform-android/package.json +++ b/packages/platform-android/package.json @@ -13,6 +13,7 @@ "@agent-device/xml": "workspace:*" }, "devDependencies": { + "@agent-device/test-kit": "workspace:*", "fast-check": "^4.9.0" }, "exports": { diff --git a/packages/platform-android/src/__tests__/index-dispatch-disclosure.test.ts b/packages/platform-android/src/__tests__/index-dispatch-disclosure.test.ts index c2ac1bad5c..935e0b1cef 100644 --- a/packages/platform-android/src/__tests__/index-dispatch-disclosure.test.ts +++ b/packages/platform-android/src/__tests__/index-dispatch-disclosure.test.ts @@ -7,7 +7,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { androidAdbResultError, withAndroidAdbProvider, diff --git a/packages/platform-android/src/__tests__/touch-helper-session.test.ts b/packages/platform-android/src/__tests__/touch-helper-session.test.ts index 7e7945c979..02643178bf 100644 --- a/packages/platform-android/src/__tests__/touch-helper-session.test.ts +++ b/packages/platform-android/src/__tests__/touch-helper-session.test.ts @@ -18,7 +18,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { withAndroidAdbProvider, type AndroidAdbProcess, diff --git a/packages/platform-android/tsconfig.json b/packages/platform-android/tsconfig.json index 90af7078cd..983e52c4fc 100644 --- a/packages/platform-android/tsconfig.json +++ b/packages/platform-android/tsconfig.json @@ -16,6 +16,7 @@ { "path": "../host-kit" }, { "path": "../kernel" }, { "path": "../provision-kit" }, + { "path": "../test-kit" }, { "path": "../xml" } ] } diff --git a/packages/platform-apple/package.json b/packages/platform-apple/package.json index ef3344106e..6b8b911ccb 100644 --- a/packages/platform-apple/package.json +++ b/packages/platform-apple/package.json @@ -96,5 +96,8 @@ "types": "./src/tool-provider-facade.ts", "default": "./src/tool-provider-facade.ts" } + }, + "devDependencies": { + "@agent-device/test-kit": "workspace:*" } } diff --git a/packages/platform-apple/src/runner/__tests__/index-dispatch-disclosure.test.ts b/packages/platform-apple/src/runner/__tests__/index-dispatch-disclosure.test.ts index b65ac30ef9..bef310980b 100644 --- a/packages/platform-apple/src/runner/__tests__/index-dispatch-disclosure.test.ts +++ b/packages/platform-apple/src/runner/__tests__/index-dispatch-disclosure.test.ts @@ -7,7 +7,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { IOS_SIMULATOR } from './device-fixtures.ts'; import { handleRunnerTransportErrorAfterCommandSend } from '../runner-command-recovery.ts'; import { isReadOnlyRunnerCommand } from '../runner-command-traits.ts'; diff --git a/packages/platform-apple/src/runner/__tests__/runner-lifecycle-dispatch-disclosure.test.ts b/packages/platform-apple/src/runner/__tests__/runner-lifecycle-dispatch-disclosure.test.ts index 7c3f6c92b7..564c28365d 100644 --- a/packages/platform-apple/src/runner/__tests__/runner-lifecycle-dispatch-disclosure.test.ts +++ b/packages/platform-apple/src/runner/__tests__/runner-lifecycle-dispatch-disclosure.test.ts @@ -6,7 +6,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { IOS_SIMULATOR } from './device-fixtures.ts'; import { createTestRequestCancellation, makeRunnerSession } from './runner-session-fixtures.ts'; import { appleRunnerTestHost } from '../test-host.ts'; diff --git a/packages/platform-apple/tsconfig.json b/packages/platform-apple/tsconfig.json index 90af7078cd..983e52c4fc 100644 --- a/packages/platform-apple/tsconfig.json +++ b/packages/platform-apple/tsconfig.json @@ -16,6 +16,7 @@ { "path": "../host-kit" }, { "path": "../kernel" }, { "path": "../provision-kit" }, + { "path": "../test-kit" }, { "path": "../xml" } ] } diff --git a/packages/platform-web/package.json b/packages/platform-web/package.json index 8c993945c4..0cf32b2dd6 100644 --- a/packages/platform-web/package.json +++ b/packages/platform-web/package.json @@ -16,5 +16,8 @@ "types": "./src/index.ts", "default": "./src/index.ts" } + }, + "devDependencies": { + "@agent-device/test-kit": "workspace:*" } } diff --git a/packages/platform-web/src/__tests__/agent-browser-provider-dispatch-disclosure.test.ts b/packages/platform-web/src/__tests__/agent-browser-provider-dispatch-disclosure.test.ts index 9fee952f52..28ed1bd15f 100644 --- a/packages/platform-web/src/__tests__/agent-browser-provider-dispatch-disclosure.test.ts +++ b/packages/platform-web/src/__tests__/agent-browser-provider-dispatch-disclosure.test.ts @@ -10,7 +10,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { AppError } from '@agent-device/kernel/errors'; import { withCommandExecutorOverride, type ExecResult } from '@agent-device/host-kit/command'; import { diff --git a/packages/platform-web/tsconfig.json b/packages/platform-web/tsconfig.json index 7647605163..59e004380a 100644 --- a/packages/platform-web/tsconfig.json +++ b/packages/platform-web/tsconfig.json @@ -15,6 +15,7 @@ { "path": "../contracts" }, { "path": "../host-kit" }, { "path": "../kernel" }, - { "path": "../selectors" } + { "path": "../selectors" }, + { "path": "../test-kit" } ] } diff --git a/packages/provider-webdriver/package.json b/packages/provider-webdriver/package.json index 05021208d9..1a6a569338 100644 --- a/packages/provider-webdriver/package.json +++ b/packages/provider-webdriver/package.json @@ -28,5 +28,8 @@ "types": "./src/connection-verification.ts", "default": "./src/connection-verification.ts" } + }, + "devDependencies": { + "@agent-device/test-kit": "workspace:*" } } diff --git a/packages/provider-webdriver/src/webdriver-orientation.test.ts b/packages/provider-webdriver/src/webdriver-orientation.test.ts index 9056116c88..c12cf988e2 100644 --- a/packages/provider-webdriver/src/webdriver-orientation.test.ts +++ b/packages/provider-webdriver/src/webdriver-orientation.test.ts @@ -10,7 +10,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; type Call = { method: string; args: unknown[] }; diff --git a/packages/provider-webdriver/src/webdriver-transport.test.ts b/packages/provider-webdriver/src/webdriver-transport.test.ts index 0a84032a38..7cfe696c18 100644 --- a/packages/provider-webdriver/src/webdriver-transport.test.ts +++ b/packages/provider-webdriver/src/webdriver-transport.test.ts @@ -12,7 +12,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { WebDriverTransport, isWebDriverConnectRefused, diff --git a/packages/provider-webdriver/tsconfig.json b/packages/provider-webdriver/tsconfig.json index dddea95d51..141ca5345a 100644 --- a/packages/provider-webdriver/tsconfig.json +++ b/packages/provider-webdriver/tsconfig.json @@ -15,6 +15,7 @@ { "path": "../contracts" }, { "path": "../host-kit" }, { "path": "../kernel" }, + { "path": "../test-kit" }, { "path": "../xml" } ] } diff --git a/packages/contracts/src/dispatch-disclosure.fixtures.ts b/packages/test-kit/src/dispatch-disclosure.fixtures.ts similarity index 100% rename from packages/contracts/src/dispatch-disclosure.fixtures.ts rename to packages/test-kit/src/dispatch-disclosure.fixtures.ts diff --git a/packages/contracts/src/dispatch-disclosure.test.ts b/packages/test-kit/src/dispatch-disclosure.test.ts similarity index 77% rename from packages/contracts/src/dispatch-disclosure.test.ts rename to packages/test-kit/src/dispatch-disclosure.test.ts index 800ef07ac1..9120b6bf59 100644 --- a/packages/contracts/src/dispatch-disclosure.test.ts +++ b/packages/test-kit/src/dispatch-disclosure.test.ts @@ -3,11 +3,6 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { test } from 'vitest'; -import { - AppError, - discloseDispatch, - discloseUnclassifiedDispatch, -} from '@agent-device/kernel/errors'; import { DISPATCH_DISCLOSURE_DRIVER_OWNERS, DISPATCH_DISCLOSURE_PRODUCERS, @@ -20,16 +15,6 @@ import { const REPO_ROOT = fileURLToPath(new URL('../../../', import.meta.url)); const DISPATCH_VALUES: readonly string[] = ['no', 'unknown']; -test('a producer verdict overwrites; the seam verdict fills only an unclassified failure', () => { - const error = new AppError('COMMAND_FAILED', 'tap failed', { hint: 'retry' }); - assert.equal(discloseUnclassifiedDispatch(error, 'unknown'), error); - assert.equal(error.details?.dispatched, 'unknown'); - assert.equal(discloseDispatch(error, 'no', { dispatchedSteps: 2 }), error); - assert.deepEqual(error.details, { hint: 'retry', dispatchedSteps: 2, dispatched: 'no' }); - discloseUnclassifiedDispatch(error, 'unknown'); - assert.equal(error.details?.dispatched, 'no'); -}); - test('dispatch-disclosure rows are unique and use the declared vocabulary', () => { const rows = parseDispatchDisclosureTable( fs.readFileSync(DISPATCH_DISCLOSURE_TABLE_PATH, 'utf8'), diff --git a/packages/test-kit/src/index.ts b/packages/test-kit/src/index.ts index 6075087256..49345c7b61 100644 --- a/packages/test-kit/src/index.ts +++ b/packages/test-kit/src/index.ts @@ -28,6 +28,11 @@ export { VISIONOS_SIMULATOR, WEB_DESKTOP_DEVICE, } from './device-fixtures.ts'; +export { + assertDispatchDisclosureDriversMatchRows, + DISPATCH_DISCLOSURE_TABLE_PATH, + dispatchDisclosureRowsOwnedBy, +} from './dispatch-disclosure.fixtures.ts'; export { restoreEnv } from './env.ts'; export { type FilesystemBoundaryFixture, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c954ab0be4..5962b25997 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -469,6 +469,9 @@ importers: specifier: workspace:* version: link:../xml devDependencies: + '@agent-device/test-kit': + specifier: workspace:* + version: link:../test-kit fast-check: specifier: ^4.9.0 version: 4.9.0 @@ -493,6 +496,10 @@ importers: '@agent-device/xml': specifier: workspace:* version: link:../xml + devDependencies: + '@agent-device/test-kit': + specifier: workspace:* + version: link:../test-kit packages/platform-harmonyos: dependencies: @@ -559,6 +566,10 @@ importers: '@agent-device/selectors': specifier: workspace:* version: link:../selectors + devDependencies: + '@agent-device/test-kit': + specifier: workspace:* + version: link:../test-kit packages/provider-limrun: dependencies: @@ -611,6 +622,10 @@ importers: '@agent-device/xml': specifier: workspace:* version: link:../xml + devDependencies: + '@agent-device/test-kit': + specifier: workspace:* + version: link:../test-kit packages/provision-kit: dependencies: diff --git a/scripts/layering/contracts-exports.snapshot.json b/scripts/layering/contracts-exports.snapshot.json index e337196f20..a4f6860006 100644 --- a/scripts/layering/contracts-exports.snapshot.json +++ b/scripts/layering/contracts-exports.snapshot.json @@ -46,7 +46,6 @@ "@agent-device/contracts/device-boot", "@agent-device/contracts/device-readiness-runtime", "@agent-device/contracts/device-shutdown-runtime", - "@agent-device/contracts/dispatch-disclosure-fixtures", "@agent-device/contracts/divergence", "@agent-device/contracts/durable-resource", "@agent-device/contracts/durable-resource-envelope", diff --git a/scripts/layering/platform-package-policy.test.ts b/scripts/layering/platform-package-policy.test.ts index 6b385fd4c2..cb5b5fc7a8 100644 --- a/scripts/layering/platform-package-policy.test.ts +++ b/scripts/layering/platform-package-policy.test.ts @@ -518,6 +518,33 @@ test('platform packages may use capture-kit but no unrelated workspace implement } }); +test('platform package tests may import test-kit, production source may not', () => { + const tests = validSources(); + tests.set( + 'packages/platform-apple/src/probe.test.ts', + "import { mkdtempForTest } from '@agent-device/test-kit';", + ); + tests.set( + 'packages/platform-apple/src/recording/fixtures.ts', + "import { mkdtempForTest } from '@agent-device/test-kit';", + ); + tests.set( + 'packages/platform-apple/src/probe.spec.ts', + "import { mkdtempForTest } from '@agent-device/test-kit';", + ); + assert.deepEqual(checkPlatformPackagePolicy(tests, declarations()), []); + + const production = validSources(); + production.set( + 'packages/platform-apple/src/probe.ts', + "import { mkdtempForTest } from '@agent-device/test-kit';", + ); + assert.match( + messages(production).join('\n'), + /may import workspace code only from capture-kit, host-kit, provision-kit, contracts, kernel, or xml/, + ); +}); + test('package-owned tests may import their own public facade', () => { const sources = validSources(); sources.set( diff --git a/scripts/layering/platform-package-policy.ts b/scripts/layering/platform-package-policy.ts index 1455280505..3fd775194d 100644 --- a/scripts/layering/platform-package-policy.ts +++ b/scripts/layering/platform-package-policy.ts @@ -175,6 +175,10 @@ function isWebPackageTestSelectorImport(file: string, family: string, specifier: ); } +function isTestKitTestImport(file: string, specifier: string): boolean { + return specifier === '@agent-device/test-kit' && !isProductionSourceFile(file); +} + function checkDeclarations(packages: readonly PlatformPackageDeclaration[]): LayeringViolation[] { const violations: LayeringViolation[] = []; for (const family of CANONICAL_PLATFORM_FAMILIES) { @@ -318,7 +322,8 @@ function checkSource(file: string, source: string): LayeringViolation[] { site.spec !== '@agent-device/xml' && !isAppleFacadeSubpathImport(site.spec) && !isPackageOwnedFacadeTest(file, ownerFamily, site.spec) && - !isWebPackageTestSelectorImport(file, ownerFamily, site.spec) + !isWebPackageTestSelectorImport(file, ownerFamily, site.spec) && + !isTestKitTestImport(file, site.spec) ) { violations.push( violation( diff --git a/src/__tests__/daemon-composition/interaction/interaction-dispatch-disclosure.test.ts b/src/__tests__/daemon-composition/interaction/interaction-dispatch-disclosure.test.ts index 5721eca17c..b3deb1ef0a 100644 --- a/src/__tests__/daemon-composition/interaction/interaction-dispatch-disclosure.test.ts +++ b/src/__tests__/daemon-composition/interaction/interaction-dispatch-disclosure.test.ts @@ -9,7 +9,7 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; +} from '@agent-device/test-kit'; import { makeSessionStore } from '@agent-device/daemon/testing/__tests__/support/store-factory'; import { makeAndroidSession } from '@agent-device/daemon/testing/__tests__/support/session-factories'; import { diff --git a/src/__tests__/daemon-composition/request-dispatch-disclosure.test.ts b/src/__tests__/daemon-composition/request-dispatch-disclosure.test.ts index a63303d194..f5467e7b8d 100644 --- a/src/__tests__/daemon-composition/request-dispatch-disclosure.test.ts +++ b/src/__tests__/daemon-composition/request-dispatch-disclosure.test.ts @@ -23,7 +23,8 @@ import { assertDispatchDisclosureDriversMatchRows, DISPATCH_DISCLOSURE_TABLE_PATH, dispatchDisclosureRowsOwnedBy, -} from '@agent-device/contracts/dispatch-disclosure-fixtures'; + mkdtempForTestSync, +} from '@agent-device/test-kit'; import { createTestDeviceInventoryGateways } from '@agent-device/device-selection/inventory-gateways-fixtures'; import { makeAndroidSession, @@ -31,7 +32,6 @@ import { makeSession, } from '@agent-device/daemon/testing/__tests__/support/session-factories'; import { makeSessionStore } from '@agent-device/daemon/testing/__tests__/support/store-factory'; -import { mkdtempForTestSync } from '@agent-device/test-kit'; import type { DaemonRequest } from '@agent-device/daemon'; import { discloseRequestDispatch } from '@agent-device/daemon/testing/request-dispatch-disclosure'; import { createRequestDispatchLedger } from '@agent-device/daemon/testing/request-dispatch-ledger';