Merge branch 'german/refactor-e12b-fake-engine-tests' into german/navara-refactor

This commit is contained in:
German Khokhlov committed 2026-11-10 17:47:29 +04:00
commit 8caff876cb
76 files changed
+668 -564

No files matched your search

+2 -1
View File
@@ -4,6 +4,7 @@
"fixedSleeps": 69,
"loadingScreenReadiness": 0,
"cesiumOnlyReads": 47,
"ownWaitHelpers": 16
"ownWaitHelpers": 16,
"testEngineLiterals": 2
}
}
+80 -1
View File
@@ -2,17 +2,19 @@ import test from 'node:test';
import assert from 'node:assert/strict';
import {
QA_HARNESS_METRICS,
engineLiteralLines,
harnessGrowth,
measureQaHarness,
readBaseline,
readQaSources,
readTestSources,
} from './report-qa-harness.mjs';
test('QA harness duplication does not grow past the baseline', () => {
assert.deepEqual(
harnessGrowth(measureQaHarness(), readBaseline().metrics),
[],
'QA harness duplication grew; use scripts/qa-strict-harness.mjs helpers for browser launch, engine readiness and camera waits instead.',
'QA duplication grew; use the shared browser harness and src/globe/testing/fakeEngine.js for test engines.',
);
});
@@ -43,6 +45,83 @@ test('QA sources include the harness and exclude tests and other reports', () =>
assert.ok(!names.includes('scripts/report-qa-harness.mjs'));
});
test('test sources include nested tests and exclude runtime files', () => {
const names = readTestSources().map(({ file }) => file);
assert.ok(names.includes('src/globe/testing/fakeEngine.test.mjs'));
assert.ok(names.includes('scripts/qaHarness.test.mjs'));
assert.ok(names.every((name) => name.endsWith('.test.mjs')));
});
test('engine literals include assignments, inline options and factory returns', () => {
for (const source of [
'const engine = {};',
'let engine; engine = { camera: {} };',
'scene.engine = { host: {} };',
"scene['engine'] = {};",
'init({ engine: {} });',
'const state = { _engine: {} };',
'const engineB = {}, trafficEngine = {};',
"function createEngine() { return { kind: 'cesium' }; }",
'const engine = () => ({ camera: {} });',
'const fakeEngine = () => { return { host: {} }; };',
'const factory = { createEngine() { return { render: {} }; } };',
'inspect({ host: { size() {} }, camera: {} });',
"const navara = { kind: 'navara', render: {} };",
])
assert.ok(engineLiteralLines(source).length > 0, source);
assert.deepEqual(
engineLiteralLines('const engine = {};\ninit({ engine: {} });'),
[1, 2],
);
});
test('engine literal counting ignores source text, options and harness observations', () => {
for (const source of [
'// const engine = {};',
"const source = 'const engine = {};';",
'const source = `init({ engine: {} });`;',
'const engine = createFakeEngine();',
'init({ engine: createFakeEngine() });',
'const options = { engineOptions: {} };',
'const engineOptions = { canvas: {}, width: 800 };',
"const headers = { host: 'localhost' };",
'const overlay = { host: { setEntries() {} } };',
'result[engine] = { camera: {} };',
'function observedEngine() { return { engine, camera }; }',
'function observedEngine() { return { snapshots: [] }; }',
'function observedEngine() { return records.map(() => ({ camera: {} })); }',
])
assert.deepEqual(engineLiteralLines(source), [], source);
});
test('engine literal metric counts test files once and rejects baseline growth', () => {
const metric = QA_HARNESS_METRICS.find(
({ id }) => id === 'testEngineLiterals',
);
const measured = metric.measure([
{
file: 'src/one.test.mjs',
text: 'const engine = {}; init({ engine: {} });',
},
{
file: 'scripts/two.test.mjs',
text: 'const engine = () => ({ host: {} });',
},
{ file: 'src/three.test.mjs', text: 'const engine = createFakeEngine();' },
{ file: 'src/runtime.js', text: 'const engine = {};' },
]);
assert.equal(measured, 2);
const baseline = Object.fromEntries(
QA_HARNESS_METRICS.map(({ id }) => [id, 0]),
);
assert.deepEqual(
harnessGrowth({ ...baseline, testEngineLiterals: measured }, baseline).map(
({ id }) => id,
),
['testEngineLiterals'],
);
});
test('metrics count scripts once and distinguish helpers from their callers', () => {
const measure = (id, ...sources) =>
QA_HARNESS_METRICS.find((metric) => metric.id === id).measure(
+106 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
/**
* Browser QA still doing work owned by scripts/qa-strict-harness.mjs.
* Each metric counts scripts, so repeated uses in one script count once.
* Browser QA and tests still duplicating the shared harness or fake engine.
* Each metric counts files, so repeated uses in one file count once.
*
* node scripts/report-qa-harness.mjs
* node scripts/report-qa-harness.mjs --json
@@ -10,6 +10,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { parsers } from 'prettier/plugins/babel.mjs';
const ROOT = fileURLToPath(new URL('..', import.meta.url));
export const BASELINE_PATH = path.join(
@@ -18,6 +19,80 @@ export const BASELINE_PATH = path.join(
'qa-harness-baseline.json',
);
/** Engine assignments, inline options and engine factory returns, not strings or comments. */
export function engineLiteralLines(text) {
const lines = [];
const engineName = (name) =>
!/Options$/.test(name ?? '') &&
/^(?:_?engine(?:[A-Z0-9]\w*)?|\w+Engine(?:[A-Z0-9]\w*)?)$/.test(name ?? '');
function walk(node, parent, factory) {
if (!node || typeof node !== 'object') return;
if (/Function|ObjectMethod/.test(node.type))
factory = node.id?.name ?? parent?.id?.name ?? node.key?.name;
if (node.type === 'ObjectExpression') {
const name =
parent?.type === 'VariableDeclarator'
? parent.id?.name
: parent?.type === 'AssignmentExpression'
? parent.left?.computed
? parent.left.property?.value
: (parent.left?.property?.name ?? parent.left?.name)
: parent?.type === 'ObjectProperty'
? (parent.key?.name ?? parent.key?.value)
: null;
const keys = node.properties.map((property) => property.key?.name);
const host = node.properties.find(
(property) => property.key?.name === 'host',
)?.value;
const globeHost =
host?.type === 'ObjectExpression' &&
host.properties.some((property) =>
['canvas', 'container', 'size'].includes(property.key?.name),
);
const adapterKind = node.properties.some(
(property) =>
property.key?.name === 'kind' &&
['cesium', 'navara'].includes(property.value?.value),
);
const returned =
parent?.type === 'ReturnStatement' ||
parent?.type === 'ArrowFunctionExpression';
// A factory's { engine, ...observations } is a harness, not its engine.
if (
engineName(name) ||
globeHost ||
(adapterKind &&
keys.some((key) =>
['camera', 'render', 'credits', 'maps'].includes(key),
)) ||
(returned &&
engineName(factory) &&
!keys.includes('engine') &&
keys.some((key) =>
[
'kind',
'host',
'camera',
'frames',
'render',
'credits',
'maps',
].includes(key),
))
)
lines.push(node.loc.start.line);
}
for (const [key, value] of Object.entries(node)) {
if (['loc', 'comments', 'tokens'].includes(key)) continue;
if (Array.isArray(value))
for (const child of value) walk(child, node, factory);
else if (value && typeof value === 'object') walk(value, node, factory);
}
}
walk(parsers.babel.parse(text));
return lines;
}
const readsCesiumState = (text) => {
const names = [
'__godsEyeView',
@@ -78,6 +153,15 @@ export const QA_HARNESS_METRICS = Object.freeze([
),
).length,
},
{
id: 'testEngineLiterals',
label: 'Test files with their own engine literal',
measure: (files) =>
files.filter(
({ file, text }) =>
file.endsWith('.test.mjs') && engineLiteralLines(text).length,
).length,
},
]);
/** QA scripts, including the shared harness itself, but excluding tests. */
@@ -92,10 +176,29 @@ export function readQaSources(root = ROOT) {
}));
}
export function readTestSources(root = ROOT) {
return ['build', 'scripts', 'server', 'src'].flatMap((directory) => {
const base = path.join(root, directory);
if (!fs.existsSync(base)) return [];
return fs
.readdirSync(base, { recursive: true })
.sort()
.filter((name) => name.endsWith('.test.mjs'))
.map((name) => ({
file: `${directory}/${name.split(path.sep).join('/')}`,
text: fs.readFileSync(path.join(base, name), 'utf8'),
}));
});
}
export function measureQaHarness(root = ROOT) {
const files = readQaSources(root);
const tests = readTestSources(root);
return Object.fromEntries(
QA_HARNESS_METRICS.map(({ id, measure }) => [id, measure(files)]),
QA_HARNESS_METRICS.map(({ id, measure }) => [
id,
measure(id === 'testEngineLiterals' ? tests : files),
]),
);
}