Improve script error dialogs #304

- Include the script's directory path #304.
- Exclude Windows-specific instructions on non-Windows OS.
- Standardize language across dialogs for consistency.

Other supporting changes:

- Add script diagnostics data collection from main process.
- Document script file storage and execution tamper protection in
  SECURITY.md.
- Remove redundant comment in `NodeReadbackFileWriter`.
- Centralize error display for uniformity and simplicity.
- Simpify `WindowVariablesValidator` to omit checks when not on the
  renderer process.
- Improve and centralize Electron environment detection.
- Use more emphatic language (don't worry) in error messages.
This commit is contained in:
undergroundwires
2024-01-17 23:59:05 +01:00
parent f03fc24098
commit 6ada8d425c
34 changed files with 1182 additions and 450 deletions

View File

@@ -2,12 +2,14 @@ import { describe, it, expect } from 'vitest';
import { CodeRunnerStub } from '@tests/unit/shared/Stubs/CodeRunnerStub';
import { ChannelDefinitionKey, IpcChannelDefinitions } from '@/presentation/electron/shared/IpcBridging/IpcChannelDefinitions';
import {
CodeRunnerFactory, DialogFactory, IpcChannelRegistrar, registerAllIpcChannels,
CodeRunnerFactory, DialogFactory, IpcChannelRegistrar,
ScriptDiagnosticsCollectorFactory, registerAllIpcChannels,
} from '@/presentation/electron/main/IpcRegistration';
import { IpcChannel } from '@/presentation/electron/shared/IpcBridging/IpcChannel';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
import { collectExceptionMessage } from '@tests/unit/shared/ExceptionCollector';
import { DialogStub } from '@tests/unit/shared/Stubs/DialogStub';
import { ScriptDiagnosticsCollectorStub } from '../../../shared/Stubs/ScriptDiagnosticsCollectorStub';
describe('IpcRegistration', () => {
describe('registerAllIpcChannels', () => {
@@ -44,6 +46,13 @@ describe('IpcRegistration', () => {
expectedInstance,
};
})(),
ScriptDiagnosticsCollector: (() => {
const expectedInstance = new ScriptDiagnosticsCollectorStub();
return {
buildContext: (c) => c.witScriptDiagnosticsCollectorFactory(() => expectedInstance),
expectedInstance,
};
})(),
};
Object.entries(testScenarios).forEach(([
key, { buildContext, expectedInstance },
@@ -79,11 +88,14 @@ describe('IpcRegistration', () => {
});
class IpcRegistrationTestSetup {
private registrar: IpcChannelRegistrar = () => { /* NOOP */ };
private codeRunnerFactory: CodeRunnerFactory = () => new CodeRunnerStub();
private dialogFactory: DialogFactory = () => new DialogStub();
private registrar: IpcChannelRegistrar = () => { /* NOOP */ };
private scriptDiagnosticsCollectorFactory
: ScriptDiagnosticsCollectorFactory = () => new ScriptDiagnosticsCollectorStub();
public withRegistrar(registrar: IpcChannelRegistrar): this {
this.registrar = registrar;
@@ -100,11 +112,19 @@ class IpcRegistrationTestSetup {
return this;
}
public witScriptDiagnosticsCollectorFactory(
scriptDiagnosticsCollectorFactory: ScriptDiagnosticsCollectorFactory,
): this {
this.scriptDiagnosticsCollectorFactory = scriptDiagnosticsCollectorFactory;
return this;
}
public run() {
registerAllIpcChannels(
this.registrar,
this.codeRunnerFactory,
this.dialogFactory,
this.registrar,
this.scriptDiagnosticsCollectorFactory,
);
}
}

View File

@@ -15,7 +15,9 @@ describe('RendererApiProvider', () => {
setupContext(context: RendererApiProviderTestContext): RendererApiProviderTestContext;
readonly expectedValue: unknown;
}
const testScenarios: Record<PropertyKeys<Required<WindowVariables>>, WindowVariableTestCase> = {
const testScenarios: Record<
PropertyKeys<Required<WindowVariables>>,
WindowVariableTestCase> = {
isRunningAsDesktopApplication: {
description: 'returns true',
setupContext: (context) => context,
@@ -32,9 +34,12 @@ describe('RendererApiProvider', () => {
})(),
log: expectFacade({
instance: new LoggerStub(),
setupContext: (c, logger) => c.withLogger(logger),
setupContext: (c, instance) => c.withLogger(instance),
}),
dialog: expectIpcConsumer(IpcChannelDefinitions.Dialog),
scriptDiagnosticsCollector: expectIpcConsumer(
IpcChannelDefinitions.ScriptDiagnosticsCollector,
),
};
Object.entries(testScenarios).forEach((
[property, { description, setupContext, expectedValue }],
@@ -109,10 +114,10 @@ class RendererApiProviderTestContext {
public provideWindowVariables() {
return provideWindowVariables(
() => this.log,
() => this.os,
this.apiFacadeCreator,
this.ipcConsumerCreator,
() => this.os,
() => this.log,
);
}
}

View File

@@ -16,6 +16,10 @@ describe('IpcChannelDefinitions', () => {
expectedNamespace: 'dialogs',
expectedAccessibleMembers: ['saveFile'],
},
ScriptDiagnosticsCollector: {
expectedNamespace: 'script-diagnostics-collector',
expectedAccessibleMembers: ['collectDiagnosticInformation'],
},
};
Object.entries(testScenarios).forEach((
[definitionKey, { expectedNamespace, expectedAccessibleMembers }],