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.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { IEnvironmentVariablesFactory } from './IEnvironmentVariablesFactory';
|
||||
import { validateEnvironmentVariables } from './EnvironmentVariablesValidator';
|
||||
import { ViteEnvironmentVariables } from './Vite/ViteEnvironmentVariables';
|
||||
import { IEnvironmentVariables } from './IEnvironmentVariables';
|
||||
|
||||
export class EnvironmentVariablesFactory implements IEnvironmentVariablesFactory {
|
||||
public static readonly Current = new EnvironmentVariablesFactory();
|
||||
|
||||
public readonly instance: IEnvironmentVariables;
|
||||
|
||||
protected constructor(validator: EnvironmentVariablesValidator = validateEnvironmentVariables) {
|
||||
const environment = new ViteEnvironmentVariables();
|
||||
validator(environment);
|
||||
this.instance = environment;
|
||||
}
|
||||
}
|
||||
|
||||
export type EnvironmentVariablesValidator = typeof validateEnvironmentVariables;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { IEnvironmentVariables } from './IEnvironmentVariables';
|
||||
|
||||
/* Validation is externalized to keep the environment objects simple */
|
||||
export function validateEnvironmentVariables(environment: IEnvironmentVariables): void {
|
||||
if (!environment) {
|
||||
throw new Error('missing environment');
|
||||
}
|
||||
const keyValues = capturePropertyValues(environment);
|
||||
if (!Object.keys(keyValues).length) {
|
||||
throw new Error('Unable to capture key/value pairs');
|
||||
}
|
||||
const keysMissingValue = getKeysMissingValues(keyValues);
|
||||
if (keysMissingValue.length > 0) {
|
||||
throw new Error(`Environment keys missing: ${keysMissingValue.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getKeysMissingValues(keyValuePairs: Record<string, unknown>): string[] {
|
||||
return Object.entries(keyValuePairs)
|
||||
.reduce((acc, [key, value]) => {
|
||||
if (!value && typeof value !== 'boolean') {
|
||||
acc.push(key);
|
||||
}
|
||||
return acc;
|
||||
}, new Array<string>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures values of properties and getters from the provided instance.
|
||||
* Necessary because code transformations can make class getters non-enumerable during bundling.
|
||||
* This ensures that even if getters are non-enumerable, their values are still captured and used.
|
||||
*/
|
||||
function capturePropertyValues(instance: unknown): Record<string, unknown> {
|
||||
const obj: Record<string, unknown> = {};
|
||||
const descriptors = Object.getOwnPropertyDescriptors(instance.constructor.prototype);
|
||||
|
||||
// Capture regular properties from the instance
|
||||
for (const [key, value] of Object.entries(instance)) {
|
||||
obj[key] = value;
|
||||
}
|
||||
|
||||
// Capture getter properties from the instance's prototype
|
||||
for (const [key, descriptor] of Object.entries(descriptors)) {
|
||||
if (typeof descriptor.get === 'function') {
|
||||
obj[key] = descriptor.get.call(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
10
src/infrastructure/EnvironmentVariables/IAppMetadata.ts
Normal file
10
src/infrastructure/EnvironmentVariables/IAppMetadata.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Represents essential metadata about the application.
|
||||
*/
|
||||
export interface IAppMetadata {
|
||||
readonly version: string;
|
||||
readonly name: string;
|
||||
readonly slogan: string;
|
||||
readonly repositoryUrl: string;
|
||||
readonly homepageUrl: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { IAppMetadata } from './IAppMetadata';
|
||||
|
||||
/**
|
||||
* Designed to decouple the process of retrieving environment variables
|
||||
* (e.g., from the build environment) from the rest of the application.
|
||||
*/
|
||||
export interface IEnvironmentVariables extends IAppMetadata {
|
||||
readonly isNonProduction: boolean;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { IEnvironmentVariables } from './IEnvironmentVariables';
|
||||
|
||||
export interface IEnvironmentVariablesFactory {
|
||||
readonly instance: IEnvironmentVariables;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Only variables prefixed with VITE_ are exposed to Vite-processed code
|
||||
export const VITE_USER_DEFINED_ENVIRONMENT_KEYS = {
|
||||
VERSION: 'VITE_APP_VERSION',
|
||||
NAME: 'VITE_APP_NAME',
|
||||
SLOGAN: 'VITE_APP_SLOGAN',
|
||||
REPOSITORY_URL: 'VITE_APP_REPOSITORY_URL',
|
||||
HOMEPAGE_URL: 'VITE_APP_HOMEPAGE_URL',
|
||||
} as const;
|
||||
|
||||
export const VITE_ENVIRONMENT_KEYS = {
|
||||
...VITE_USER_DEFINED_ENVIRONMENT_KEYS,
|
||||
DEV: 'DEV',
|
||||
} as const;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { IEnvironmentVariables } from '../IEnvironmentVariables';
|
||||
|
||||
/**
|
||||
* Provides the application's environment variables.
|
||||
*/
|
||||
export class ViteEnvironmentVariables implements IEnvironmentVariables {
|
||||
// Ensure the use of import.meta.env prefix for the following properties.
|
||||
// Vue will replace these statically during production builds.
|
||||
|
||||
public get version(): string {
|
||||
return import.meta.env.VITE_APP_VERSION;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return import.meta.env.VITE_APP_NAME;
|
||||
}
|
||||
|
||||
public get slogan(): string {
|
||||
return import.meta.env.VITE_APP_SLOGAN;
|
||||
}
|
||||
|
||||
public get repositoryUrl(): string {
|
||||
return import.meta.env.VITE_APP_REPOSITORY_URL;
|
||||
}
|
||||
|
||||
public get homepageUrl(): string {
|
||||
return import.meta.env.VITE_APP_HOMEPAGE_URL;
|
||||
}
|
||||
|
||||
public get isNonProduction(): boolean {
|
||||
return import.meta.env.DEV;
|
||||
}
|
||||
}
|
||||
11
src/infrastructure/EnvironmentVariables/Vite/vite-env.d.ts
vendored
Normal file
11
src/infrastructure/EnvironmentVariables/Vite/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import { VITE_ENVIRONMENT_KEYS } from './ViteEnvironmentKeys';
|
||||
|
||||
export type ViteEnvironmentVariables = {
|
||||
readonly [K in keyof typeof VITE_ENVIRONMENT_KEYS]: string;
|
||||
};
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ViteEnvironmentVariables
|
||||
}
|
||||
Reference in New Issue
Block a user