mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Merge branch 'german/refactor-e12b-fake-engine-tests' into german/navara-refactor
This commit is contained in:
commit
8caff876cb
76 files changed
+668
-564
No files matched your search
@@ -4,6 +4,7 @@
|
||||
"fixedSleeps": 69,
|
||||
"loadingScreenReadiness": 0,
|
||||
"cesiumOnlyReads": 47,
|
||||
"ownWaitHelpers": 16
|
||||
"ownWaitHelpers": 16,
|
||||
"testEngineLiterals": 2
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user