Files
openless/openless-all/app/scripts/macos-speech-usage-description-contract.test.mjs

100 lines
2.9 KiB
JavaScript

import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process';
if (process.platform !== 'darwin') {
console.log('macOS speech usage description contract skipped on non-macOS');
process.exit(0);
}
const scriptsDir = fileURLToPath(new URL('.', import.meta.url));
const checker = join(scriptsDir, 'check-macos-speech-usage-description.sh');
const fixtureDir = mkdtempSync(join(tmpdir(), 'openless-speech-usage-'));
function plist(value) {
return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>${value}</dict></plist>
`;
}
function writeFixture(name, value) {
const path = join(fixtureDir, name);
writeFileSync(path, plist(value));
return path;
}
function runChecker(path, env = {}) {
return spawnSync('bash', [checker, path], {
encoding: 'utf8',
env: { ...process.env, ...env },
});
}
function expectSuccessSilent(name, path) {
const result = runChecker(path);
if (result.status !== 0) {
throw new Error(`${name}: expected exit 0, got ${result.status}\n${result.stderr}`);
}
if (result.stdout !== '' || result.stderr !== '') {
throw new Error(
`${name}: success must be silent, got ${JSON.stringify(result.stdout + result.stderr)}`,
);
}
}
function expectFailure(name, path, env = {}) {
const result = runChecker(path, env);
if (result.status === 0) {
throw new Error(`${name}: expected non-zero exit`);
}
}
try {
expectSuccessSilent(
'valid string',
writeFixture(
'valid.plist',
'<key>NSSpeechRecognitionUsageDescription</key><string>OpenLess transcribes speech locally.</string>',
),
);
expectFailure('missing key', writeFixture('missing-key.plist', ''));
expectFailure(
'empty string',
writeFixture(
'empty-string.plist',
'<key>NSSpeechRecognitionUsageDescription</key><string></string>',
),
);
expectFailure(
'whitespace-only string',
writeFixture(
'whitespace-string.plist',
'<key>NSSpeechRecognitionUsageDescription</key><string> \n\t </string>',
),
);
for (const [name, slug, whitespace] of [
['NBSP', 'nbsp', '\u00a0'],
['EM SPACE', 'em-space', '\u2003'],
['IDEOGRAPHIC SPACE', 'ideographic-space', '\u3000'],
]) {
expectFailure(
`${name}-only string under C locale`,
writeFixture(
`${slug}-only.plist`,
`<key>NSSpeechRecognitionUsageDescription</key><string>${whitespace}</string>`,
),
{ LC_ALL: 'C', LANG: 'C' },
);
}
expectFailure(
'wrong type',
writeFixture('wrong-type.plist', '<key>NSSpeechRecognitionUsageDescription</key><true/>'),
);
expectFailure('missing file', join(fixtureDir, 'does-not-exist.plist'));
} finally {
rmSync(fixtureDir, { recursive: true, force: true });
}