Refactor to enforce strictNullChecks
This commit applies `strictNullChecks` to the entire codebase to improve maintainability and type safety. Key changes include: - Remove some explicit null-checks where unnecessary. - Add necessary null-checks. - Refactor static factory functions for a more functional approach. - Improve some test names and contexts for better debugging. - Add unit tests for any additional logic introduced. - Refactor `createPositionFromRegexFullMatch` to its own function as the logic is reused. - Prefer `find` prefix on functions that may return `undefined` and `get` prefix for those that always return a value.
This commit is contained in:
@@ -63,9 +63,24 @@ async function determineLogPath(
|
||||
const logFilePaths: {
|
||||
readonly [K in SupportedPlatform]: () => string;
|
||||
} = {
|
||||
[SupportedPlatform.macOS]: () => join(process.env.HOME, 'Library', 'Logs', appName, `${logFileName}.log`),
|
||||
[SupportedPlatform.Linux]: () => join(process.env.HOME, '.config', appName, 'logs', `${logFileName}.log`),
|
||||
[SupportedPlatform.Windows]: () => join(process.env.USERPROFILE, 'AppData', 'Roaming', appName, 'logs', `${logFileName}.log`),
|
||||
[SupportedPlatform.macOS]: () => {
|
||||
if (!process.env.HOME) {
|
||||
throw new Error('HOME environment variable is not defined');
|
||||
}
|
||||
return join(process.env.HOME, 'Library', 'Logs', appName, `${logFileName}.log`);
|
||||
},
|
||||
[SupportedPlatform.Linux]: () => {
|
||||
if (!process.env.HOME) {
|
||||
throw new Error('HOME environment variable is not defined');
|
||||
}
|
||||
return join(process.env.HOME, '.config', appName, 'logs', `${logFileName}.log`);
|
||||
},
|
||||
[SupportedPlatform.Windows]: () => {
|
||||
if (!process.env.USERPROFILE) {
|
||||
throw new Error('USERPROFILE environment variable is not defined');
|
||||
}
|
||||
return join(process.env.USERPROFILE, 'AppData', 'Roaming', appName, 'logs', `${logFileName}.log`);
|
||||
},
|
||||
};
|
||||
const logFilePath = logFilePaths[CURRENT_PLATFORM]?.();
|
||||
if (!logFilePath) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { splitTextIntoLines, indentText } from '../utils/text';
|
||||
import { splitTextIntoLines, indentText, filterEmpty } from '../utils/text';
|
||||
import { log, die } from '../utils/log';
|
||||
import { readAppLogFile } from './app-logs';
|
||||
import { STDERR_IGNORE_PATTERNS } from './error-ignore-patterns';
|
||||
@@ -33,7 +33,7 @@ async function gatherErrors(
|
||||
if (!projectDir) { throw new Error('missing project directory'); }
|
||||
const { logFileContent: mainLogs, logFilePath: mainLogFile } = await readAppLogFile(projectDir, 'main');
|
||||
const { logFileContent: rendererLogs, logFilePath: rendererLogFile } = await readAppLogFile(projectDir, 'renderer');
|
||||
const allLogs = [mainLogs, rendererLogs, stderr].filter(Boolean).join('\n');
|
||||
const allLogs = filterEmpty([mainLogs, rendererLogs, stderr]).join('\n');
|
||||
return [
|
||||
verifyStdErr(stderr),
|
||||
verifyApplicationLogsExist('main', mainLogs, mainLogFile),
|
||||
@@ -43,7 +43,7 @@ async function gatherErrors(
|
||||
),
|
||||
verifyWindowTitle(windowTitles),
|
||||
verifyErrorsInLogs(allLogs),
|
||||
].filter(Boolean);
|
||||
].filter((error): error is ExecutionError => Boolean(error));
|
||||
}
|
||||
|
||||
interface ExecutionError {
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function findByFilePattern(
|
||||
pattern: string,
|
||||
directory: string,
|
||||
projectRootDir: string,
|
||||
): Promise<ArtifactLocation> {
|
||||
): Promise<ArtifactLocation | never> {
|
||||
if (!directory) { throw new Error('Missing directory'); }
|
||||
if (!pattern) { throw new Error('Missing file pattern'); }
|
||||
|
||||
@@ -42,5 +42,5 @@ function escapeRegExp(string: string) {
|
||||
}
|
||||
|
||||
interface ArtifactLocation {
|
||||
readonly absolutePath?: string;
|
||||
readonly absolutePath: string;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ async function mountDmg(
|
||||
die(`Failed to mount DMG file at ${dmgFile}.\n${error}`);
|
||||
}
|
||||
const mountPathMatch = hdiutilOutput.match(/\/Volumes\/[^\n]+/);
|
||||
const mountPath = mountPathMatch ? mountPathMatch[0] : null;
|
||||
if (!mountPathMatch || mountPathMatch.length === 0) {
|
||||
die(`Could not find mount path from \`hdiutil\` output:\n${hdiutilOutput}`);
|
||||
}
|
||||
const mountPath = mountPathMatch[0];
|
||||
return {
|
||||
mountPath,
|
||||
};
|
||||
@@ -52,7 +55,10 @@ async function findMacAppExecutablePath(
|
||||
return die(`Failed to find executable path at mount path ${mountPath}\n${error}`);
|
||||
}
|
||||
const appFolder = findOutput.trim();
|
||||
const appName = appFolder.split('/').pop().replace('.app', '');
|
||||
const appName = appFolder.split('/').pop()?.replace('.app', '');
|
||||
if (!appName) {
|
||||
die(`Could not extract app path from \`find\` output: ${findOutput}`);
|
||||
}
|
||||
const appPath = `${appFolder}/Contents/MacOS/${appName}`;
|
||||
if (await exists(appPath)) {
|
||||
log(`Application is located at ${appPath}`);
|
||||
|
||||
@@ -20,6 +20,7 @@ export function runApplication(
|
||||
|
||||
logDetails(appFile, executionDurationInSeconds);
|
||||
|
||||
const process = spawn(appFile);
|
||||
const processDetails: ApplicationProcessDetails = {
|
||||
stderrData: '',
|
||||
stdoutData: '',
|
||||
@@ -27,15 +28,15 @@ export function runApplication(
|
||||
windowTitles: [],
|
||||
isCrashed: false,
|
||||
isDone: false,
|
||||
process: undefined,
|
||||
process,
|
||||
resolve: () => { /* NOOP */ },
|
||||
};
|
||||
|
||||
const process = spawn(appFile);
|
||||
processDetails.process = process;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
processDetails.resolve = resolve;
|
||||
if (process.pid === undefined) {
|
||||
throw new Error('Unknown PID');
|
||||
}
|
||||
beginCapturingTitles(process.pid, processDetails);
|
||||
handleProcessEvents(
|
||||
processDetails,
|
||||
@@ -54,13 +55,14 @@ interface ApplicationExecutionResult {
|
||||
}
|
||||
|
||||
interface ApplicationProcessDetails {
|
||||
readonly process: ChildProcess;
|
||||
|
||||
stderrData: string;
|
||||
stdoutData: string;
|
||||
explicitlyKilled: boolean;
|
||||
windowTitles: Array<string>;
|
||||
isCrashed: boolean;
|
||||
isDone: boolean;
|
||||
process: ChildProcess;
|
||||
resolve: (value: ApplicationExecutionResult) => void;
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ function beginCapturingTitles(
|
||||
const titles = await captureWindowTitles(processId);
|
||||
|
||||
(titles || []).forEach((title) => {
|
||||
if (!title?.length) {
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
if (!processDetails.windowTitles.includes(title)) {
|
||||
@@ -109,10 +111,10 @@ function handleProcessEvents(
|
||||
executionDurationInSeconds: number,
|
||||
): void {
|
||||
const { process } = processDetails;
|
||||
process.stderr.on('data', (data) => {
|
||||
process.stderr?.on('data', (data) => {
|
||||
processDetails.stderrData += data.toString();
|
||||
});
|
||||
process.stdout.on('data', (data) => {
|
||||
process.stdout?.on('data', (data) => {
|
||||
processDetails.stdoutData += data.toString();
|
||||
});
|
||||
|
||||
@@ -130,7 +132,7 @@ function handleProcessEvents(
|
||||
}
|
||||
|
||||
async function onProcessExit(
|
||||
code: number,
|
||||
code: number | null,
|
||||
processDetails: ApplicationProcessDetails,
|
||||
enableScreenshot: boolean,
|
||||
screenshotPath: string,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { runCommand } from '../../utils/run-command';
|
||||
import { log, LogLevel } from '../../utils/log';
|
||||
import { SupportedPlatform, CURRENT_PLATFORM } from '../../utils/platform';
|
||||
import { filterEmpty } from '../../utils/text';
|
||||
|
||||
export async function captureWindowTitles(processId: number) {
|
||||
if (!processId) { throw new Error('Missing process ID.'); }
|
||||
@@ -67,7 +68,7 @@ async function captureTitlesOnLinux(processId: number): Promise<string[]> {
|
||||
return titleOutput.trim();
|
||||
}));
|
||||
|
||||
return titles.filter(Boolean);
|
||||
return filterEmpty(titles);
|
||||
}
|
||||
|
||||
let hasAssistiveAccessOnMac = true;
|
||||
|
||||
Reference in New Issue
Block a user