mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
i18n: fix review findings and polish ru/es strings
Fixes the 51 P0–P2 findings from the full review of the i18n branch and the issues found while checking every new string on screen in ru and es. - Translate errors and statuses that still reached ru/es in English: feed and tile errors, provider statuses, the ALPR chip, the traffic mode chip, voice plan step labels, fire feed values, receiver bands, scene save reasons and the Cesium attribution link. - Show preset city names in the UI language (src/ui/placeNames.js); data and everything sent to the model stay English. - Share layer names between the layer panel, HUD and voice card (src/ui/layerNames.js). - Keep long ru/es strings whole: voice card error lines, analyst titles, the voice tier status, the Nepal evidence button, the ALPR retry chip. English layout is unchanged. - Ask before a language switch drops unsaved work. - Catalog fixes: glossary terms (пуск, пресет), gender agreement and consistent "peticiones" in es, fewer calques in both languages. - Fix English text that was itself wrong (e.g. "Exit cockpit to fly to a weather", raw tool ids in voice plan steps). Catalogs: es/ru 2535 messages each, complete. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8029747b7e
commit
00704ddc89
212 files changed
+9871
-909
No files matched your search
+76
-35
@@ -11,7 +11,7 @@
|
||||
// Messages are the English literals passed to t(), tn() and msg() in src/,
|
||||
// plus the markup opted in with data-i18n* in src/ui/templates and index.html.
|
||||
// It also lists UI text that is not marked at all (see findUnmarked).
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -62,23 +62,27 @@ function decodeHtml(text) {
|
||||
const normalize = (text) => text.replace(/\s+/g, ' ').trim();
|
||||
|
||||
function trackedFiles(...pathspecs) {
|
||||
return execFileSync(
|
||||
'git',
|
||||
[
|
||||
'ls-files',
|
||||
'--cached',
|
||||
'--others',
|
||||
'--exclude-standard',
|
||||
'--',
|
||||
...pathspecs,
|
||||
],
|
||||
{
|
||||
cwd: ROOT,
|
||||
encoding: 'utf8',
|
||||
},
|
||||
)
|
||||
.split('\n')
|
||||
.filter(Boolean);
|
||||
return (
|
||||
execFileSync(
|
||||
'git',
|
||||
[
|
||||
'ls-files',
|
||||
'--cached',
|
||||
'--others',
|
||||
'--exclude-standard',
|
||||
'--',
|
||||
...pathspecs,
|
||||
],
|
||||
{
|
||||
cwd: ROOT,
|
||||
encoding: 'utf8',
|
||||
},
|
||||
)
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
// The index still lists a file moved or deleted without git mv / git rm.
|
||||
.filter((file) => existsSync(path.join(ROOT, file)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,8 +217,8 @@ const JS_SINKS = [
|
||||
/\bsetAttribute\(\s*['"](?:title|aria-label|aria-valuetext|placeholder|alt)['"]\s*,\s*/g,
|
||||
/\b(?:createTextNode|insertAdjacentText)\(\s*(?:['"][a-z]+['"]\s*,\s*)?/g,
|
||||
/\binsertAdjacentHTML\(\s*['"][a-z]+['"]\s*,\s*/g,
|
||||
/\b(?:showToast|toast|setHint|announce)\(\s*/g,
|
||||
/(?:^|[{,\s])(?:label|title|tooltip|placeholder|hint|ariaLabel|caption|subtitle|kicker|emptyText|blurb|countLabel)\s*:\s*/gm,
|
||||
/\b(?:showToast|toast|setHint|announce|say)\(\s*/g,
|
||||
/(?:^|[{,\s])(?:label|title|tooltip|placeholder|hint|ariaLabel|accessibilityLabel|caption|subtitle|kicker|emptyText|blurb|countLabel)\s*:\s*/gm,
|
||||
];
|
||||
const MARKERS = /\b(?:t|tn|msg)\(/y;
|
||||
|
||||
@@ -438,6 +442,15 @@ export function readSameAsEnglish(locale) {
|
||||
/** The English a message shows: its text without the "context|" prefix. */
|
||||
const englishOf = (text) => text.slice(text.indexOf('|') + 1);
|
||||
|
||||
/** A translation with nothing to show: t() falls back to English for it. */
|
||||
const blank = (value) => value.trim() === '';
|
||||
|
||||
/** Words to read, beyond {placeholders}, codes and punctuation. */
|
||||
const hasLetters = (value) => /\p{L}{2}/u.test(value.replace(/\{[^}]*\}/g, ''));
|
||||
|
||||
const STILL_ENGLISH = (text, locale) =>
|
||||
`"${text}" is still English: translate it, or list it under "${locale}" in src/i18n/same-as-english.json`;
|
||||
|
||||
/**
|
||||
* Compare one catalog with the extracted messages.
|
||||
* @param {Set<string>} [sameAsEnglish] Messages allowed to read as English.
|
||||
@@ -464,44 +477,72 @@ export function checkCatalog(
|
||||
errors.push(`"${text}" needs plural forms ${categories.join('/')}`);
|
||||
continue;
|
||||
}
|
||||
const english = new Set([info.one, englishOf(text)]);
|
||||
const stillEnglish = [];
|
||||
for (const category of categories) {
|
||||
if (typeof entry[category] !== 'string')
|
||||
const form = entry[category];
|
||||
if (typeof form !== 'string')
|
||||
errors.push(`"${text}" lacks the "${category}" form`);
|
||||
else if (placeholders(entry[category]) !== placeholders(text))
|
||||
errors.push(
|
||||
`"${text}" [${category}] placeholders differ: ${entry[category]}`,
|
||||
);
|
||||
else if (blank(form))
|
||||
errors.push(`"${text}" [${category}] is empty: translate it`);
|
||||
else if (placeholders(form) !== placeholders(text))
|
||||
errors.push(`"${text}" [${category}] placeholders differ: ${form}`);
|
||||
else if (english.has(form) && hasLetters(form))
|
||||
stillEnglish.push(category);
|
||||
}
|
||||
if (stillEnglish.length && !sameAsEnglish.has(text))
|
||||
errors.push(
|
||||
`${STILL_ENGLISH(text, locale)} (forms ${stillEnglish.join('/')})`,
|
||||
);
|
||||
} else if (typeof entry !== 'string') {
|
||||
errors.push(`"${text}" must be a string`);
|
||||
} else if (blank(entry)) {
|
||||
errors.push(`"${text}" is empty: translate it`);
|
||||
} else if (placeholders(entry) !== placeholders(text)) {
|
||||
errors.push(`"${text}" placeholders differ: ${entry}`);
|
||||
} else if (
|
||||
entry === englishOf(text) &&
|
||||
/\p{L}{2}/u.test(entry.replace(/\{[^}]*\}/g, '')) &&
|
||||
hasLetters(entry) &&
|
||||
!sameAsEnglish.has(text)
|
||||
) {
|
||||
errors.push(
|
||||
`"${text}" is still English: translate it, or list it under "${locale}" in src/i18n/same-as-english.json`,
|
||||
);
|
||||
errors.push(STILL_ENGLISH(text, locale));
|
||||
}
|
||||
}
|
||||
const stale = Object.keys(catalog).filter((text) => !messages.has(text));
|
||||
return { missing, stale, errors };
|
||||
}
|
||||
|
||||
/**
|
||||
* What `--missing <locale>` prints: every missing entry, ready to translate.
|
||||
* A template, not a translation: the check rejects a string left empty and a
|
||||
* plural form left as the English.
|
||||
*/
|
||||
export function missingTemplate(locale, catalog, messages) {
|
||||
const categories = new Intl.PluralRules(locale).resolvedOptions()
|
||||
.pluralCategories;
|
||||
const out = {};
|
||||
for (const text of checkCatalog(locale, catalog, messages, new Set())
|
||||
.missing) {
|
||||
const info = messages.get(text);
|
||||
out[text] = info.plural
|
||||
? Object.fromEntries(
|
||||
categories.map((category) => [
|
||||
category,
|
||||
category === 'one' ? info.one : text,
|
||||
]),
|
||||
)
|
||||
: '';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function main(args) {
|
||||
const messages = extractMessages();
|
||||
const missingFor = args.includes('--missing')
|
||||
? args[args.indexOf('--missing') + 1]
|
||||
: null;
|
||||
if (missingFor) {
|
||||
const catalog = readCatalog(missingFor);
|
||||
const out = {};
|
||||
for (const text of checkCatalog(missingFor, catalog, messages).missing) {
|
||||
const info = messages.get(text);
|
||||
out[text] = info.plural ? { one: info.one, other: text } : '';
|
||||
}
|
||||
const out = missingTemplate(missingFor, readCatalog(missingFor), messages);
|
||||
console.log(JSON.stringify(out, null, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -439,6 +439,7 @@
|
||||
"modules": [
|
||||
"src/ui/layers.js",
|
||||
"src/ui/layerPanel.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/ui/weatherPanel.js",
|
||||
"src/ui/railCards.js",
|
||||
"src/ui/railCardBlocks.js",
|
||||
@@ -467,6 +468,7 @@
|
||||
"src/ui/location.js",
|
||||
"src/ui/locationControls.js",
|
||||
"src/ui/locationSearch.js",
|
||||
"src/ui/placeNames.js",
|
||||
"src/locationStatus.js",
|
||||
"src/app/stateChannel.js",
|
||||
"src/i18n/index.js",
|
||||
@@ -527,7 +529,8 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/ru.json"
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/ui/layerNames.js"
|
||||
],
|
||||
"external": []
|
||||
},
|
||||
@@ -581,6 +584,7 @@
|
||||
"ui-shell": {
|
||||
"exports": ["./ui/shell"],
|
||||
"modules": [
|
||||
"src/ui/placeNames.js",
|
||||
"src/hudLayouts.js",
|
||||
"src/hudLayoutPolicy.js",
|
||||
"src/cyberSonar.js",
|
||||
@@ -602,6 +606,7 @@
|
||||
"src/data/layerStateCoordinator.js",
|
||||
"src/data/layerStateTokenReservations.json",
|
||||
"src/data/militaryAwarenessEngine.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/data/scenePick.js",
|
||||
"src/keySetupCore.mjs",
|
||||
"src/localRequestGate.mjs",
|
||||
@@ -719,7 +724,8 @@
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/ui/unitLabels.js",
|
||||
"src/ui/keyGuidance.js"
|
||||
"src/ui/keyGuidance.js",
|
||||
"src/ui/layerNames.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -786,6 +792,7 @@
|
||||
"src/data/bhoteKoshiFloodPath.js",
|
||||
"src/maps/catalog.js",
|
||||
"src/scenes/director.js",
|
||||
"src/scenes/sceneSaveError.js",
|
||||
"src/scenes/playbackAdapter.js",
|
||||
"src/director/playback.js",
|
||||
"src/scenes/recipes.js",
|
||||
@@ -1078,7 +1085,8 @@
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/ru.json"
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/ui/unitLabels.js"
|
||||
],
|
||||
"external": ["cesium", "satellite.js"]
|
||||
},
|
||||
@@ -1119,6 +1127,7 @@
|
||||
"src/data/cameraSensitivity.js",
|
||||
"src/data/installationFeedback.js",
|
||||
"src/data/militaryInstallationData.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/layers/installations/controls.js",
|
||||
"src/layers/installations/index.js",
|
||||
"src/layers/installations/ingestion.js",
|
||||
@@ -1160,6 +1169,7 @@
|
||||
"src/cockpitMath.js",
|
||||
"src/data/iconOrientation.js",
|
||||
"src/data/militaryAwarenessEngine.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/layers/awareness/controls.js",
|
||||
"src/layers/awareness/dependencies.js",
|
||||
"src/layers/awareness/focus.js",
|
||||
@@ -1219,6 +1229,7 @@
|
||||
"src/sources/capability.js",
|
||||
"src/sources/httpBody.js",
|
||||
"src/sources/openFreeMap.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/sources/militaryTileGeometry.js",
|
||||
"src/sources/vectorTiles.js",
|
||||
"src/sources/phaseTiming.js",
|
||||
@@ -1537,6 +1548,9 @@
|
||||
"src/voice/referents.js",
|
||||
"src/voice/layerManifest.js",
|
||||
"src/voice/speech.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/ui/unitLabels.js",
|
||||
"src/ui/placeNames.js",
|
||||
"src/voice/narration.js",
|
||||
"src/voice/earcon.js",
|
||||
"src/voice/pointerContext.js",
|
||||
@@ -1568,6 +1582,7 @@
|
||||
"./standalone/catalog"
|
||||
],
|
||||
"modules": [
|
||||
"src/ui/placeNames.js",
|
||||
"src/cyberSonarScene.js",
|
||||
"src/cyberSonar.js",
|
||||
"src/hudLayouts.js",
|
||||
@@ -1710,6 +1725,7 @@
|
||||
"src/data/meshFloorSampler.js",
|
||||
"src/data/militaryAwareness.js",
|
||||
"src/data/militaryAwarenessEngine.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/data/militaryFlights.js",
|
||||
"src/data/militaryInstallationData.js",
|
||||
"src/data/militaryInstallations.js",
|
||||
@@ -2005,6 +2021,7 @@
|
||||
"src/renderGovernor.js",
|
||||
"src/rightRailPolicy.js",
|
||||
"src/scenes/director.js",
|
||||
"src/scenes/sceneSaveError.js",
|
||||
"src/scenes/playbackAdapter.js",
|
||||
"src/director/playback.js",
|
||||
"src/scenes/recipes.js",
|
||||
@@ -2123,6 +2140,7 @@
|
||||
"src/voice/control.js",
|
||||
"src/voice/gevActions.js",
|
||||
"src/voice/speech.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/voice/earcon.js",
|
||||
"src/voice/narration.js",
|
||||
"src/voice/turnMetrics.js",
|
||||
@@ -2156,6 +2174,7 @@
|
||||
"src/ui/geoFileImport.js",
|
||||
"src/data/routeSteps.js",
|
||||
"src/layers/directions/index.js",
|
||||
"src/data/pointerOwnerNames.js",
|
||||
"src/layers/directions/source.js",
|
||||
"src/data/directions.js",
|
||||
"src/app/layers/directions.js",
|
||||
@@ -2486,6 +2505,7 @@
|
||||
"src/data/local_data/natural_earth/regions.json",
|
||||
"src/data/local_data/neighborhoods/san-francisco.json",
|
||||
"src/data/militaryAwarenessEngine.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/data/militaryInstallationData.js",
|
||||
"src/data/modelVisualAnchor.js",
|
||||
"src/data/motionModel.js",
|
||||
@@ -2767,6 +2787,7 @@
|
||||
"src/services/groundFloor.js",
|
||||
"src/data/routeSteps.js",
|
||||
"src/layers/directions/index.js",
|
||||
"src/data/pointerOwnerNames.js",
|
||||
"src/layers/directions/source.js",
|
||||
"src/services/terrainHeights.js",
|
||||
"src/data/terrainHeights.js",
|
||||
@@ -3016,7 +3037,11 @@
|
||||
"src/navigationPolicy.js",
|
||||
"src/ui/navigationController.js",
|
||||
"src/app/stateChannel.js",
|
||||
"src/data/cameraArrival.js"
|
||||
"src/data/cameraArrival.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/ru.json",
|
||||
"src/i18n/index.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -3170,6 +3195,7 @@
|
||||
"exports": ["./layers/directions"],
|
||||
"modules": [
|
||||
"src/layers/directions/index.js",
|
||||
"src/data/pointerOwnerNames.js",
|
||||
"src/layers/directions/source.js",
|
||||
"src/data/routeSteps.js",
|
||||
"src/i18n/index.js",
|
||||
@@ -3212,6 +3238,7 @@
|
||||
"voice-session-controls": {
|
||||
"exports": ["./voice/session-controls"],
|
||||
"modules": [
|
||||
"src/ui/placeNames.js",
|
||||
"src/voice/sessionCommands.js",
|
||||
"src/voice/session.js",
|
||||
"src/voice/control.js",
|
||||
@@ -3221,6 +3248,8 @@
|
||||
"src/voice/referents.js",
|
||||
"src/voice/layerManifest.js",
|
||||
"src/voice/speech.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/ui/unitLabels.js",
|
||||
"src/voice/pointerReticle.js",
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
@@ -3271,6 +3300,7 @@
|
||||
"modules": [
|
||||
"src/layers/installations/source.js",
|
||||
"src/data/militaryInstallationData.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/data/tomtomTiles.js",
|
||||
"src/sources/capability.js",
|
||||
"src/sources/httpBody.js",
|
||||
@@ -3312,6 +3342,7 @@
|
||||
"src/sources/overpassRoads.js",
|
||||
"src/sources/httpBody.js",
|
||||
"src/sources/openFreeMap.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/sources/militaryTileGeometry.js",
|
||||
"src/sources/vectorTiles.js",
|
||||
"src/sources/phaseTiming.js",
|
||||
@@ -3496,7 +3527,11 @@
|
||||
"scripts/qa-transit-controls.mjs",
|
||||
"scripts/qa-transit-browser.mjs",
|
||||
"src/layers/transit/source.js",
|
||||
"src/sources/transitHistory.js"
|
||||
"src/sources/transitHistory.js",
|
||||
"src/i18n/index.js",
|
||||
"src/i18n/locale.js",
|
||||
"src/i18n/locales/es.json",
|
||||
"src/i18n/locales/ru.json"
|
||||
],
|
||||
"external": ["cesium", "pbf"]
|
||||
},
|
||||
@@ -3670,6 +3705,7 @@
|
||||
"src/data/retryableLoad.js",
|
||||
"src/data/local_data/osm_military_names/names.json",
|
||||
"src/sources/openFreeMap.js",
|
||||
"src/data/installationNames.js",
|
||||
"src/sources/vectorTiles.js",
|
||||
"src/sources/phaseTiming.js",
|
||||
"src/data/tomtomTiles.js",
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* qa-scene-dialog-file-drop — the SHARE SCENE dialog's file control accepts a
|
||||
* file dropped from the desktop, proved in a real browser.
|
||||
*
|
||||
* The dialog replaces the browser-drawn "Choose files / No file chosen" line
|
||||
* with a translated one (src/ui/sceneSharing.js, fileChooser). The native
|
||||
* input must stay the whole control underneath it: a shrunken 1 px input
|
||||
* accepts drops only on its own pixel, so dragging a data-pack file onto the
|
||||
* visible row selected nothing. This harness drops a file onto the centre of
|
||||
* the visible row through CDP and expects `input.files` to hold it.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/qa-scene-dialog-file-drop.mjs
|
||||
*
|
||||
* Needs the repository's puppeteer (devDependencies). Exit status 0 when the
|
||||
* drop selects the file, 1 otherwise.
|
||||
*/
|
||||
import http from 'node:http';
|
||||
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const TYPES = {
|
||||
'.js': 'text/javascript',
|
||||
'.mjs': 'text/javascript',
|
||||
'.json': 'application/json',
|
||||
'.html': 'text/html',
|
||||
};
|
||||
|
||||
const PAGE = `<!doctype html><html lang="en"><body>
|
||||
<script type="module">
|
||||
import { createSceneDialog } from '/src/ui/sceneSharing.js';
|
||||
window.openDialog = () => {
|
||||
const dialog = createSceneDialog('Share selected scene', () => {});
|
||||
const files = dialog.input('Choose data-pack files', '', { type: 'file' });
|
||||
files.multiple = true;
|
||||
window.filesInput = files;
|
||||
// Centre of the visible row (the wrapper's last child).
|
||||
const row = files.parentElement.lastElementChild;
|
||||
const r = row.getBoundingClientRect();
|
||||
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
||||
};
|
||||
window.ready = true;
|
||||
</script></body></html>`;
|
||||
|
||||
async function dropOn(client, x, y, file) {
|
||||
const data = { items: [], files: [file], dragOperationsMask: 1 };
|
||||
for (const type of ['dragEnter', 'dragOver', 'drop']) {
|
||||
await client.send('Input.dispatchDragEvent', { type, x, y, data });
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const dir = await mkdtemp(path.join(tmpdir(), 'qa-scene-dialog-drop-'));
|
||||
const pack = path.join(dir, 'pack.json');
|
||||
await writeFile(pack, '{}');
|
||||
const server = http.createServer(async (req, res) => {
|
||||
if (req.url === '/') {
|
||||
res.writeHead(200, { 'content-type': 'text/html' });
|
||||
res.end(PAGE);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const body = await readFile(path.join(ROOT, req.url));
|
||||
res.writeHead(200, {
|
||||
'content-type': TYPES[path.extname(req.url)] || 'text/plain',
|
||||
});
|
||||
res.end(body);
|
||||
} catch {
|
||||
res.writeHead(404).end();
|
||||
}
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
||||
const url = `http://127.0.0.1:${server.address().port}/`;
|
||||
const browser = await puppeteer.launch({ headless: true });
|
||||
try {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1000, height: 800 });
|
||||
const errors = [];
|
||||
page.on('pageerror', (error) => errors.push(String(error)));
|
||||
await page.goto(url);
|
||||
await page.waitForFunction(() => window.ready === true);
|
||||
const point = await page.evaluate(() => window.openDialog());
|
||||
const client = await page.createCDPSession();
|
||||
await dropOn(client, point.x, point.y, pack);
|
||||
const names = await page.evaluate(() =>
|
||||
[...window.filesInput.files].map((file) => file.name),
|
||||
);
|
||||
console.log(JSON.stringify({ names, errors }));
|
||||
if (errors.length) throw new Error(`page errors: ${errors.join('; ')}`);
|
||||
if (names.length !== 1 || names[0] !== 'pack.json') {
|
||||
throw new Error(
|
||||
`expected the dropped pack.json to be selected, got ${JSON.stringify(names)}`,
|
||||
);
|
||||
}
|
||||
console.log('OK: a file dropped on the visible control is selected');
|
||||
} finally {
|
||||
await browser.close();
|
||||
server.close();
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in new issue
Block a user