mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
cb65d6ca1f
* refactor(tests): replace the test-utils barrel with direct module imports The barrel re-exported 13 modules, so every importer evaluated all of them (store-factory alone drags 16 daemon session-store files; property-arbitraries drags fast-check). Importing the backing modules directly cuts the unit suite's aggregate eager module evaluations from 153,401 to 144,344 (-5.9%), measured with the eager-import-closure walker. Deleting the barrel makes the tax unrepresentable instead of pinning it with a guard test. * docs(testing): point fixture guidance at the test-utils modules, not the deleted barrel * test: extract replay session fixture
157 lines
5.6 KiB
TypeScript
157 lines
5.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
import { WEB_DESKTOP_DEVICE } from '../../src/__tests__/test-utils/device-fixtures.ts';
|
|
import { mkdtempForTest } from '../../src/__tests__/test-utils/tmp-dir.ts';
|
|
import { PUBLIC_COMMANDS } from '../../src/command-catalog.ts';
|
|
import { isCommandSupportedOnDevice } from '../../src/core/capabilities.ts';
|
|
import {
|
|
WEB_COVERAGE_GAP_ISSUE,
|
|
WEB_PLATFORM_COVERAGE,
|
|
WEB_PLATFORM_COVERAGE_CLASSIFICATION_SUMMARY,
|
|
WEB_SMOKE_EVIDENCE,
|
|
WEB_SMOKE_TEST_NAME,
|
|
liveCommandsForWebSmoke,
|
|
} from './web-e2e/coverage-manifest.ts';
|
|
import { runCleanupWithCoverageReport, writeCoverageReport } from './web-e2e/coverage-report.ts';
|
|
|
|
const publicCommands = Object.values(PUBLIC_COMMANDS).sort();
|
|
|
|
test('web coverage exhaustively classifies the public catalog', () => {
|
|
assert.deepEqual(Object.keys(WEB_PLATFORM_COVERAGE).sort(), publicCommands);
|
|
|
|
for (const command of publicCommands) {
|
|
const entry = WEB_PLATFORM_COVERAGE[command];
|
|
assert.ok(entry.assertion.trim().length > 0, `${command} needs an observable assertion`);
|
|
if (entry.level === 'live' || entry.level === 'command-contract') {
|
|
assert.ok(entry.owner.path.trim().length > 0, `${command} needs an evidence path`);
|
|
assert.ok(entry.owner.test.trim().length > 0, `${command} needs named evidence`);
|
|
}
|
|
if (entry.level === 'known-gap') {
|
|
assert.equal(
|
|
entry.trackingIssue,
|
|
WEB_COVERAGE_GAP_ISSUE,
|
|
`${command} has the wrong gap issue`,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('web coverage report has the expected classification counts', () => {
|
|
assert.deepEqual(WEB_PLATFORM_COVERAGE_CLASSIFICATION_SUMMARY, {
|
|
capabilityDenial: 15,
|
|
contract: 12,
|
|
gap: 15,
|
|
live: 12,
|
|
total: 54,
|
|
});
|
|
});
|
|
|
|
test('web live claims reference commands in the existing smoke scenario', () => {
|
|
const smokeSource = fs.readFileSync(path.resolve(WEB_SMOKE_EVIDENCE.path), 'utf8');
|
|
assert.ok(smokeSource.includes(WEB_SMOKE_TEST_NAME));
|
|
|
|
const liveCommands = liveCommandsForWebSmoke();
|
|
assert.equal(liveCommands.length, 12);
|
|
for (const command of liveCommands) {
|
|
assert.equal(
|
|
smokeSource.includes(`'${command}',`),
|
|
true,
|
|
`${command} is not invoked by ${WEB_SMOKE_EVIDENCE.path}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('web contract claims name existing executable evidence', () => {
|
|
for (const [command, entry] of Object.entries(WEB_PLATFORM_COVERAGE)) {
|
|
if (entry.level !== 'command-contract') continue;
|
|
const evidencePath = path.resolve(entry.owner.path);
|
|
assert.equal(fs.existsSync(evidencePath), true, `${command} owner does not exist`);
|
|
assert.equal(
|
|
fs.readFileSync(evidencePath, 'utf8').includes(entry.owner.test),
|
|
true,
|
|
`${command} owner does not contain named evidence`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('web capability denials match the mechanical capability matrix', () => {
|
|
const deniedByCapabilities = publicCommands.filter(
|
|
(command) => !isCommandSupportedOnDevice(command, WEB_DESKTOP_DEVICE),
|
|
);
|
|
const deniedByManifest = Object.entries(WEB_PLATFORM_COVERAGE)
|
|
.filter(([, entry]) => entry.level === 'capability-denial')
|
|
.map(([command]) => command)
|
|
.sort();
|
|
|
|
assert.deepEqual(deniedByManifest, deniedByCapabilities);
|
|
});
|
|
|
|
test('web known gaps use one grouped tracking issue', () => {
|
|
const gapIssues = new Set(
|
|
Object.values(WEB_PLATFORM_COVERAGE)
|
|
.filter((entry) => entry.level === 'known-gap')
|
|
.map((entry) => entry.trackingIssue),
|
|
);
|
|
assert.deepEqual([...gapIssues], [WEB_COVERAGE_GAP_ISSUE]);
|
|
});
|
|
|
|
test('web coverage report persists the manifest rollup and live command list', async () => {
|
|
const artifactDir = await mkdtempForTest('agent-device-web-coverage-');
|
|
const steps = [{ command: 'agent-device open http://127.0.0.1 --platform web', status: 0 }];
|
|
const report = JSON.parse(readCoverageReport(artifactDir, steps)) as {
|
|
classificationSummary: typeof WEB_PLATFORM_COVERAGE_CLASSIFICATION_SUMMARY;
|
|
liveCommands: string[];
|
|
steps: typeof steps;
|
|
};
|
|
|
|
assert.deepEqual(report.classificationSummary, WEB_PLATFORM_COVERAGE_CLASSIFICATION_SUMMARY);
|
|
assert.deepEqual(report.liveCommands.sort(), liveCommandsForWebSmoke().sort());
|
|
assert.deepEqual(report.steps, steps);
|
|
});
|
|
|
|
test('web coverage report survives a failed close cleanup', async () => {
|
|
const artifactDir = await mkdtempForTest('agent-device-web-close-failure-');
|
|
const steps = [{ command: 'agent-device close --platform web', status: 1 }];
|
|
const closeError = new Error('close failed');
|
|
|
|
await assert.rejects(
|
|
runCleanupWithCoverageReport(artifactDir, steps, async () => {
|
|
throw closeError;
|
|
}),
|
|
(error) => error === closeError,
|
|
);
|
|
|
|
const report = JSON.parse(
|
|
fs.readFileSync(path.join(artifactDir, 'coverage-report.json'), 'utf8'),
|
|
) as { steps: typeof steps };
|
|
assert.deepEqual(report.steps, steps);
|
|
});
|
|
|
|
test('web cleanup error is preserved when report writing also fails', async () => {
|
|
const artifactDir = await mkdtempForTest('agent-device-web-report-failure-');
|
|
const cleanupError = new Error('close failed');
|
|
|
|
await assert.rejects(
|
|
runCleanupWithCoverageReport(path.join(artifactDir, 'missing-directory'), [], async () => {
|
|
throw cleanupError;
|
|
}),
|
|
(error) => {
|
|
assert.ok(error instanceof AggregateError);
|
|
assert.equal(error.errors[0], cleanupError);
|
|
assert.ok(error.errors[1] instanceof Error);
|
|
return true;
|
|
},
|
|
);
|
|
});
|
|
|
|
function readCoverageReport(
|
|
artifactDir: string,
|
|
steps: readonly { command: string; status: number }[],
|
|
) {
|
|
const reportPath = writeCoverageReport(artifactDir, steps);
|
|
return fs.readFileSync(reportPath, 'utf8');
|
|
}
|