Files
privacy.sexy/scripts/check-desktop-runtime-errors/app/system-capture/window-title-capture.ts
undergroundwires ad0576a752 Improve desktop runtime execution tests
Test improvements:

- Capture titles for all macOS windows, not just the frontmost.
- Incorporate missing application log files.
- Improve log clarity with enriched context.
- Improve application termination on macOS by reducing grace period.
- Ensure complete application termination on macOS.
- Validate Vue application loading through an initial log.
- Support ignoring environment-specific `stderr` errors.
- Do not fail the test if working directory cannot be deleted.
- Use retry pattern when installing dependencies due to network errors.

Refactorings:

- Migrate the test code to TypeScript.
- Replace deprecated `rmdir` with `rm` for error-resistant directory
  removal.
- Improve sanity checking by shifting from App.vue to Vue bootstrapper.
- Centralize environment variable management with `EnvironmentVariables`
  construct.
- Rename infrastructure/Environment to RuntimeEnvironment for clarity.
- Isolate WindowVariables and SystemOperations from RuntimeEnvironment.
- Inject logging via preloader.
- Correct mislabeled RuntimeSanity tests.

Configuration:

- Introduce `npm run check:desktop` for simplified execution.
- Omit `console.log` override due to `nodeIntegration` restrictions and
  reveal logging functionality using context-bridging.
2023-08-29 16:30:00 +02:00

118 lines
3.5 KiB
TypeScript

import { runCommand } from '../../utils/run-command';
import { log, LogLevel } from '../../utils/log';
import { SupportedPlatform, CURRENT_PLATFORM } from '../../utils/platform';
export async function captureWindowTitles(processId: number) {
if (!processId) { throw new Error('Missing process ID.'); }
const captureFunction = windowTitleCaptureFunctions[CURRENT_PLATFORM];
if (!captureFunction) {
log(`Cannot capture window title, unsupported OS: ${SupportedPlatform[CURRENT_PLATFORM]}`, LogLevel.Warn);
return undefined;
}
return captureFunction(processId);
}
const windowTitleCaptureFunctions: {
readonly [K in SupportedPlatform]: (processId: number) => Promise<string[]>;
} = {
[SupportedPlatform.macOS]: (processId) => captureTitlesOnMac(processId),
[SupportedPlatform.Linux]: (processId) => captureTitlesOnLinux(processId),
[SupportedPlatform.Windows]: (processId) => captureTitlesOnWindows(processId),
};
async function captureTitlesOnWindows(processId: number): Promise<string[]> {
if (!processId) { throw new Error('Missing process ID.'); }
const { stdout: tasklistOutput, error } = await runCommand(
`tasklist /FI "PID eq ${processId}" /fo list /v`,
);
if (error) {
log(`Failed to retrieve window title.\n${error}`, LogLevel.Warn);
return [];
}
const regex = /Window Title:\s*(.*)/;
const match = regex.exec(tasklistOutput);
if (match && match.length > 1 && match[1]) {
const title = match[1].trim();
if (title === 'N/A') {
return [];
}
return [title];
}
return [];
}
async function captureTitlesOnLinux(processId: number): Promise<string[]> {
if (!processId) { throw new Error('Missing process ID.'); }
const { stdout: windowIdsOutput, error: windowIdError } = await runCommand(
`xdotool search --pid '${processId}'`,
);
if (windowIdError || !windowIdsOutput) {
return [];
}
const windowIds = windowIdsOutput.trim().split('\n');
const titles = await Promise.all(windowIds.map(async (windowId) => {
const { stdout: titleOutput, error: titleError } = await runCommand(
`xprop -id ${windowId} | grep "WM_NAME(STRING)" | cut -d '=' -f 2 | sed 's/^[[:space:]]*"\\(.*\\)"[[:space:]]*$/\\1/'`,
);
if (titleError || !titleOutput) {
return undefined;
}
return titleOutput.trim();
}));
return titles.filter(Boolean);
}
let hasAssistiveAccessOnMac = true;
async function captureTitlesOnMac(processId: number): Promise<string[]> {
if (!processId) { throw new Error('Missing process ID.'); }
if (!hasAssistiveAccessOnMac) {
return [];
}
const script = `
tell application "System Events"
try
set targetProcess to first process whose unix id is ${processId}
on error
return
end try
tell targetProcess
set allWindowNames to {}
repeat with aWindow in windows
set end of allWindowNames to name of aWindow
end repeat
return allWindowNames
end tell
end tell
`;
const argument = script.trim()
.split(/[\r\n]+/)
.map((line) => `-e '${line.trim()}'`)
.join(' ');
const { stdout: titleOutput, error } = await runCommand(`osascript ${argument}`);
if (error) {
let errorMessage = '';
if (error.includes('-25211')) {
errorMessage += 'Capturing window title requires assistive access. You do not have it.\n';
hasAssistiveAccessOnMac = false;
}
errorMessage += error;
log(errorMessage, LogLevel.Warn);
return [];
}
const title = titleOutput?.trim();
if (!title) {
return [];
}
return [title];
}