Bump to TypeScript 5.5 and enable noImplicitAny
This commit upgrades TypeScript from 5.4 to 5.5 and enables the
`noImplicitAny` option for stricter type checking. It refactors code to
comply with `noImplicitAny` and adapts to new TypeScript features and
limitations.
Key changes:
- Migrate from TypeScript 5.4 to 5.5
- Enable `noImplicitAny` for stricter type checking
- Refactor code to comply with new TypeScript features and limitations
Other supporting changes:
- Refactor progress bar handling for type safety
- Drop 'I' prefix from interfaces to align with new code convention
- Update TypeScript target from `ES2017` and `ES2018`.
This allows named capturing groups. Otherwise, new TypeScript compiler
does not compile the project and shows the following error:
```
...
TimestampedFilenameGenerator.spec.ts:105:23 - error TS1503: Named capturing groups are only available when targeting 'ES2018' or later
const pattern = /^(?<timestamp>\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})-(?<scriptName>[^.]+?)(?:\.(?<extension>[^.]+))?$/;// timestamp-scriptName.extension
...
```
- Refactor usage of `electron-progressbar` for type safety and
less complexity.
This commit is contained in:
@@ -78,10 +78,12 @@ function setCommandLineFlagsFromEnvironmentVariables() {
|
||||
};
|
||||
Object.entries(flagEnvironmentVariableKeyMappings)
|
||||
.forEach(([flag, environmentVariableKey]) => {
|
||||
const flagValue = Number.parseInt(flag, 10) as CommandLineFlag;
|
||||
const flagDefinition = COMMAND_LINE_FLAGS[flagValue];
|
||||
if (process.env[environmentVariableKey] !== undefined) {
|
||||
process.argv = [
|
||||
...process.argv,
|
||||
COMMAND_LINE_FLAGS[flag],
|
||||
flagDefinition,
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe } from 'vitest';
|
||||
import type { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/ISanityCheckOptions';
|
||||
import type { SanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/SanityCheckOptions';
|
||||
import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChecks';
|
||||
import { isBoolean } from '@/TypeHelpers';
|
||||
|
||||
@@ -21,8 +21,8 @@ describe('SanityChecks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function generateTestOptions(): ISanityCheckOptions[] {
|
||||
const defaultOptions: ISanityCheckOptions = {
|
||||
function generateTestOptions(): SanityCheckOptions[] {
|
||||
const defaultOptions: SanityCheckOptions = {
|
||||
validateEnvironmentVariables: true,
|
||||
validateWindowVariables: true,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { it, expect } from 'vitest';
|
||||
import type { ISanityValidator } from '@/infrastructure/RuntimeSanity/Common/ISanityValidator';
|
||||
import type { SanityValidator } from '@/infrastructure/RuntimeSanity/Common/SanityValidator';
|
||||
|
||||
export function itNoErrorsOnCurrentEnvironment(
|
||||
factory: () => ISanityValidator,
|
||||
factory: () => SanityValidator,
|
||||
) {
|
||||
it('it does report errors on current environment', () => {
|
||||
// arrange
|
||||
|
||||
@@ -129,7 +129,11 @@ function createSampleNodes(): TreeInputNodeDataWithMetadata[] {
|
||||
];
|
||||
}
|
||||
|
||||
function waitForStableDom(rootElement, timeout = 3000, interval = 200): Promise<void> {
|
||||
function waitForStableDom(
|
||||
rootElement: Node,
|
||||
timeout = 3000,
|
||||
interval = 200,
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let lastTimeoutId: ReturnType<typeof setTimeout>;
|
||||
const observer = new MutationObserver(() => {
|
||||
|
||||
@@ -33,7 +33,7 @@ function removeUndefinedProperties(obj: object | undefined): object | undefined
|
||||
return obj;
|
||||
}
|
||||
return Object.keys(obj).reduce((acc, key) => {
|
||||
const value = obj[key];
|
||||
const value = obj[key as keyof (typeof obj)];
|
||||
switch (typeof value) {
|
||||
case 'object': {
|
||||
const cleanValue = removeUndefinedProperties(value); // recurse
|
||||
|
||||
@@ -4,7 +4,9 @@ export type SupportedOperatingSystem = OperatingSystem.Windows
|
||||
| OperatingSystem.Linux
|
||||
| OperatingSystem.macOS;
|
||||
|
||||
export const AllSupportedOperatingSystems: readonly OperatingSystem[] = [
|
||||
export const AllSupportedOperatingSystems: readonly (
|
||||
OperatingSystem & SupportedOperatingSystem
|
||||
)[] = [
|
||||
OperatingSystem.Windows,
|
||||
OperatingSystem.Linux,
|
||||
OperatingSystem.macOS,
|
||||
|
||||
@@ -25,9 +25,10 @@ describe('PlatformTimer', () => {
|
||||
describe('setTimeout', () => {
|
||||
it('calls the global setTimeout with the provided delay', () => {
|
||||
// arrange
|
||||
type Delay = Parameters<typeof setTimeout>[1];
|
||||
const expectedDelay = 55;
|
||||
let actualDelay: number | undefined;
|
||||
global.setTimeout = ((_, delay) => {
|
||||
let actualDelay: Delay | undefined;
|
||||
global.setTimeout = ((_: never, delay: Delay) => {
|
||||
actualDelay = delay;
|
||||
}) as typeof global.setTimeout;
|
||||
// act
|
||||
@@ -37,9 +38,10 @@ describe('PlatformTimer', () => {
|
||||
});
|
||||
it('calls the global setTimeout with the provided callback', () => {
|
||||
// arrange
|
||||
type Callback = Parameters<typeof setTimeout>[0];
|
||||
const expectedCallback = () => { /* NOOP */ };
|
||||
let actualCallback: typeof expectedCallback | undefined;
|
||||
global.setTimeout = ((callback) => {
|
||||
let actualCallback: Callback | undefined;
|
||||
global.setTimeout = ((callback: Callback) => {
|
||||
actualCallback = callback;
|
||||
}) as typeof global.setTimeout;
|
||||
// act
|
||||
@@ -52,8 +54,9 @@ describe('PlatformTimer', () => {
|
||||
describe('clearTimeout', () => {
|
||||
it('should clear timeout', () => {
|
||||
// arrange
|
||||
let actualTimer: ReturnType<typeof PlatformTimer.setTimeout> | undefined;
|
||||
global.clearTimeout = ((timer) => {
|
||||
type Timer = ReturnType<typeof PlatformTimer.setTimeout>;
|
||||
let actualTimer: Timer | undefined;
|
||||
global.clearTimeout = ((timer: Timer) => {
|
||||
actualTimer = timer;
|
||||
}) as typeof global.clearTimeout;
|
||||
const expectedTimer = PlatformTimer.setTimeout(() => { /* NOOP */ }, 1);
|
||||
|
||||
@@ -50,16 +50,33 @@ describe('ApplicationContext', () => {
|
||||
// assert
|
||||
expectEmptyState(sut.state);
|
||||
});
|
||||
it('throws when OS is unknown to application', () => {
|
||||
it('rethrows when application cannot provide collection for supported OS', () => {
|
||||
// arrange
|
||||
const expectedError = 'expected error from application';
|
||||
const applicationStub = new ApplicationStub();
|
||||
const initialOs = OperatingSystem.Android;
|
||||
const targetOs = OperatingSystem.ChromeOS;
|
||||
const context = new ObservableApplicationContextFactory()
|
||||
.withAppContainingCollections(initialOs, targetOs)
|
||||
.withInitialOs(initialOs);
|
||||
// act
|
||||
const sut = context.construct();
|
||||
const { app } = context;
|
||||
app.getCollection = () => { throw new Error(expectedError); };
|
||||
const act = () => sut.changeContext(targetOs);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('throws when OS state is unknown to application', () => {
|
||||
// arrange
|
||||
const knownOs = OperatingSystem.Android;
|
||||
const unknownOs = OperatingSystem.ChromeOS;
|
||||
const expectedError = `Operating system "${OperatingSystem[unknownOs]}" state is unknown.`;
|
||||
const sut = new ObservableApplicationContextFactory()
|
||||
.withApp(applicationStub)
|
||||
.withAppContainingCollections(knownOs)
|
||||
.withInitialOs(knownOs)
|
||||
.construct();
|
||||
// act
|
||||
applicationStub.getCollection = () => { throw new Error(expectedError); };
|
||||
const act = () => sut.changeContext(OperatingSystem.Android);
|
||||
const act = () => sut.changeContext(unknownOs);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -181,14 +198,28 @@ describe('ApplicationContext', () => {
|
||||
const actual = sut.state.os;
|
||||
expect(actual).to.deep.equal(expected);
|
||||
});
|
||||
it('throws when OS is unknown to application', () => {
|
||||
it('rethrows when application cannot provide collection for supported OS', () => {
|
||||
// arrange
|
||||
const expectedError = 'expected error from application';
|
||||
const applicationStub = new ApplicationStub();
|
||||
applicationStub.getCollection = () => { throw new Error(expectedError); };
|
||||
const knownOperatingSystem = OperatingSystem.macOS;
|
||||
const context = new ObservableApplicationContextFactory()
|
||||
.withAppContainingCollections(knownOperatingSystem)
|
||||
.withInitialOs(knownOperatingSystem);
|
||||
const { app } = context;
|
||||
app.getCollection = () => { throw new Error(expectedError); };
|
||||
// act
|
||||
const act = () => context.construct();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('throws when OS is not supported', () => {
|
||||
// arrange
|
||||
const unknownInitialOperatingSystem = OperatingSystem.BlackBerry10;
|
||||
const expectedError = `Operating system "${OperatingSystem[unknownInitialOperatingSystem]}" is not supported.`;
|
||||
// act
|
||||
const act = () => new ObservableApplicationContextFactory()
|
||||
.withApp(applicationStub)
|
||||
.withAppContainingCollections(OperatingSystem.Android /* unrelated */)
|
||||
.withInitialOs(unknownInitialOperatingSystem)
|
||||
.construct();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
@@ -222,24 +253,24 @@ class ObservableApplicationContextFactory {
|
||||
|
||||
private initialOs = ObservableApplicationContextFactory.DefaultOs;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
this.withAppContainingCollections(ObservableApplicationContextFactory.DefaultOs);
|
||||
}
|
||||
|
||||
public withAppContainingCollections(
|
||||
...oses: OperatingSystem[]
|
||||
): ObservableApplicationContextFactory {
|
||||
): this {
|
||||
const collectionValues = oses.map((os) => new CategoryCollectionStub().withOs(os));
|
||||
const app = new ApplicationStub().withCollections(...collectionValues);
|
||||
return this.withApp(app);
|
||||
}
|
||||
|
||||
public withApp(app: IApplication): ObservableApplicationContextFactory {
|
||||
public withApp(app: IApplication): this {
|
||||
this.app = app;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withInitialOs(initialOs: OperatingSystem) {
|
||||
public withInitialOs(initialOs: OperatingSystem): this {
|
||||
this.initialOs = initialOs;
|
||||
return this;
|
||||
}
|
||||
@@ -250,6 +281,7 @@ class ObservableApplicationContextFactory {
|
||||
return sut;
|
||||
}
|
||||
}
|
||||
|
||||
function getDuplicates<T>(list: readonly T[]): T[] {
|
||||
return list.filter((item, index) => list.indexOf(item) !== index);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('CategoryCollectionState', () => {
|
||||
describe('selection', () => {
|
||||
it('initializes with empty scripts', () => {
|
||||
// arrange
|
||||
const expectedScripts = [];
|
||||
const expectedScripts: readonly SelectedScript[] = [];
|
||||
let actualScripts: readonly SelectedScript[] | undefined;
|
||||
const selectionFactoryMock: SelectionFactory = (_, scripts) => {
|
||||
actualScripts = scripts;
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('ApplicationCode', () => {
|
||||
describe('ctor', () => {
|
||||
it('empty when selection is empty', () => {
|
||||
// arrange
|
||||
const selectedScripts = [];
|
||||
const selectedScripts: readonly SelectedScript[] = [];
|
||||
const selection = new ScriptSelectionStub()
|
||||
.withSelectedScripts(selectedScripts);
|
||||
const definition = new ScriptingDefinitionStub();
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ScriptingDefinitionStub } from '@tests/unit/shared/Stubs/ScriptingDefin
|
||||
import { itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
|
||||
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
|
||||
describe('UserScriptGenerator', () => {
|
||||
describe('scriptingDefinition', () => {
|
||||
@@ -143,7 +144,7 @@ describe('UserScriptGenerator', () => {
|
||||
it('without script; returns empty', () => {
|
||||
// arrange
|
||||
const sut = new UserScriptGenerator();
|
||||
const selectedScripts = [];
|
||||
const selectedScripts: readonly SelectedScript[] = [];
|
||||
const definition = new ScriptingDefinitionStub();
|
||||
// act
|
||||
const actual = sut.buildCode(selectedScripts, definition);
|
||||
|
||||
@@ -279,7 +279,7 @@ describe('DebouncedScriptSelection', () => {
|
||||
it('throws error when an empty script array is passed', () => {
|
||||
// arrange
|
||||
const expectedError = 'Provided script array is empty. To deselect all scripts, please use the deselectAll() method instead.';
|
||||
const scripts = [];
|
||||
const scripts: readonly Script[] = [];
|
||||
const scriptSelection = new DebouncedScriptSelectionBuilder().build();
|
||||
// act
|
||||
const act = () => scriptSelection.selectOnly(scripts);
|
||||
|
||||
@@ -135,7 +135,7 @@ describe('createTypeValidator', () => {
|
||||
});
|
||||
it('throws error for empty collection', () => {
|
||||
// arrange
|
||||
const emptyArrayValue = [];
|
||||
const emptyArrayValue: unknown[] = [];
|
||||
const valueName = 'empty collection value';
|
||||
const expectedMessage = `'${valueName}' cannot be an empty array.`;
|
||||
const { assertNonEmptyCollection } = createTypeValidator();
|
||||
@@ -251,7 +251,7 @@ describe('createTypeValidator', () => {
|
||||
});
|
||||
|
||||
function createObjectWithProperties(properties: readonly string[]): object {
|
||||
const object = {};
|
||||
const object: Record<string, unknown> = {};
|
||||
properties.forEach((propertyName) => {
|
||||
object[propertyName] = 'arbitrary value';
|
||||
});
|
||||
|
||||
@@ -383,7 +383,7 @@ function createExpressionFactorySpy() {
|
||||
};
|
||||
return {
|
||||
createExpression,
|
||||
getInitParameters: (expression) => createdExpressions.get(expression),
|
||||
getInitParameters: (expression: IExpression) => createdExpressions.get(expression),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { PipeFactory } from '@/application/Parser/Executable/Script/Compiler/Expressions/Pipes/PipeFactory';
|
||||
import { PipeStub } from '@tests/unit/shared/Stubs/PipeStub';
|
||||
import { getAbsentStringTestCases } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import type { Pipe } from '@/application/Parser/Executable/Script/Compiler/Expressions/Pipes/Pipe';
|
||||
|
||||
describe('PipeFactory', () => {
|
||||
describe('ctor', () => {
|
||||
@@ -49,7 +50,7 @@ describe('PipeFactory', () => {
|
||||
// arrange
|
||||
const missingName = 'missingName';
|
||||
const expectedError = `Unknown pipe: "${missingName}"`;
|
||||
const pipes = [];
|
||||
const pipes: readonly Pipe[] = [];
|
||||
const sut = new PipeFactory(pipes);
|
||||
// act
|
||||
const act = () => sut.get(missingName);
|
||||
|
||||
@@ -9,20 +9,20 @@ import { getAbsentStringTestCases } from '@tests/unit/shared/TestCases/AbsentTes
|
||||
describe('PipelineCompiler', () => {
|
||||
describe('compile', () => {
|
||||
describe('throws for invalid arguments', () => {
|
||||
interface ITestCase {
|
||||
interface ThrowingPipeScenario {
|
||||
readonly name: string;
|
||||
readonly act: (test: PipelineTestRunner) => PipelineTestRunner;
|
||||
readonly expectedError: string;
|
||||
}
|
||||
const testCases: ITestCase[] = [
|
||||
const testScenarios: ThrowingPipeScenario[] = [
|
||||
...getAbsentStringTestCases({ excludeNull: true, excludeUndefined: true })
|
||||
.map((testCase) => ({
|
||||
.map((testCase): ThrowingPipeScenario => ({
|
||||
name: `"value" is ${testCase.valueName}`,
|
||||
act: (test) => test.withValue(testCase.absentValue),
|
||||
expectedError: 'missing value',
|
||||
})),
|
||||
...getAbsentStringTestCases({ excludeNull: true, excludeUndefined: true })
|
||||
.map((testCase) => ({
|
||||
.map((testCase): ThrowingPipeScenario => ({
|
||||
name: `"pipeline" is ${testCase.valueName}`,
|
||||
act: (test) => test.withPipeline(testCase.absentValue),
|
||||
expectedError: 'missing pipeline',
|
||||
@@ -33,7 +33,7 @@ describe('PipelineCompiler', () => {
|
||||
expectedError: 'pipeline does not start with pipe',
|
||||
},
|
||||
];
|
||||
for (const testCase of testCases) {
|
||||
for (const testCase of testScenarios) {
|
||||
it(testCase.name, () => {
|
||||
// act
|
||||
const runner = new PipelineTestRunner();
|
||||
|
||||
@@ -114,7 +114,7 @@ export class SyntaxParserTestsRunner {
|
||||
}
|
||||
}
|
||||
|
||||
interface ExpectResultTestScenario {
|
||||
export interface ExpectResultTestScenario {
|
||||
readonly name: string;
|
||||
readonly code: string;
|
||||
readonly args: (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe } from 'vitest';
|
||||
import { ExpressionPosition } from '@/application/Parser/Executable/Script/Compiler/Expressions/Expression/ExpressionPosition';
|
||||
import { WithParser } from '@/application/Parser/Executable/Script/Compiler/Expressions/SyntaxParsers/WithParser';
|
||||
import { getAbsentStringTestCases } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import { SyntaxParserTestsRunner } from './SyntaxParserTestsRunner';
|
||||
import { SyntaxParserTestsRunner, type ExpectResultTestScenario } from './SyntaxParserTestsRunner';
|
||||
|
||||
describe('WithParser', () => {
|
||||
const sut = new WithParser();
|
||||
@@ -120,7 +120,7 @@ describe('WithParser', () => {
|
||||
describe('does not render scope', () => {
|
||||
runner.expectResults(
|
||||
...getAbsentStringTestCases({ excludeNull: true, excludeUndefined: true })
|
||||
.map((testCase) => ({
|
||||
.map((testCase): ExpectResultTestScenario => ({
|
||||
name: `does not render when value is "${testCase.valueName}"`,
|
||||
code: '{{ with $parameter }}dark{{ end }} ',
|
||||
args: (args) => args
|
||||
@@ -138,7 +138,7 @@ describe('WithParser', () => {
|
||||
describe('renders scope', () => {
|
||||
runner.expectResults(
|
||||
...getAbsentStringTestCases({ excludeNull: true, excludeUndefined: true })
|
||||
.map((testCase) => ({
|
||||
.map((testCase): ExpectResultTestScenario => ({
|
||||
name: `does not render when value is "${testCase.valueName}"`,
|
||||
code: '{{ with $parameter }}dark{{ end }} ',
|
||||
args: (args) => args
|
||||
|
||||
@@ -165,11 +165,14 @@ function createScriptLanguageScenarios(): readonly ScriptLanguageScenario[] {
|
||||
[ScriptingLanguage.batchfile]: 8191,
|
||||
[ScriptingLanguage.shellscript]: 1048576,
|
||||
};
|
||||
return Object.entries(maxLengths).map(([language, length]): ScriptLanguageScenario => ({
|
||||
description: `${ScriptingLanguage[language]} (max: ${length})`,
|
||||
language: Number.parseInt(language, 10) as ScriptingLanguage,
|
||||
maxLength: length,
|
||||
}));
|
||||
return Object.entries(maxLengths).map(([language, length]): ScriptLanguageScenario => {
|
||||
const languageValue = Number.parseInt(language, 10) as ScriptingLanguage;
|
||||
return {
|
||||
description: `${ScriptingLanguage[languageValue]} (max: ${length})`,
|
||||
language: languageValue,
|
||||
maxLength: length,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
class TestContext {
|
||||
|
||||
@@ -37,9 +37,9 @@ describe('TimestampedFilenameGenerator', () => {
|
||||
// act
|
||||
const filename = generateFilenamePartsForTesting({ date });
|
||||
// assert
|
||||
expect(filename.timestamp).to.equal(expectedTimestamp, formatAssertionMessage[
|
||||
`Generated filename: ${filename.generatedFilename}`
|
||||
]);
|
||||
expect(filename.timestamp).to.equal(expectedTimestamp, formatAssertionMessage([
|
||||
`Generated filename: ${filename.generatedFilename}`,
|
||||
]));
|
||||
});
|
||||
describe('extension', () => {
|
||||
it('uses correct extension', () => {
|
||||
@@ -48,9 +48,9 @@ describe('TimestampedFilenameGenerator', () => {
|
||||
// act
|
||||
const filename = generateFilenamePartsForTesting({ extension: expectedExtension });
|
||||
// assert
|
||||
expect(filename.extension).to.equal(expectedExtension, formatAssertionMessage[
|
||||
`Generated filename: ${filename.generatedFilename}`
|
||||
]);
|
||||
expect(filename.extension).to.equal(expectedExtension, formatAssertionMessage([
|
||||
`Generated filename: ${filename.generatedFilename}`,
|
||||
]));
|
||||
});
|
||||
describe('handles absent extension', () => {
|
||||
itEachAbsentStringValue((absentExtension) => {
|
||||
@@ -59,9 +59,9 @@ describe('TimestampedFilenameGenerator', () => {
|
||||
// act
|
||||
const filename = generateFilenamePartsForTesting({ extension: absentExtension });
|
||||
// assert
|
||||
expect(filename.extension).to.equal(expectedExtension, formatAssertionMessage[
|
||||
`Generated file name: ${filename.generatedFilename}`
|
||||
]);
|
||||
expect(filename.extension).to.equal(expectedExtension, formatAssertionMessage([
|
||||
`Generated file name: ${filename.generatedFilename}`,
|
||||
]));
|
||||
}, { excludeNull: true });
|
||||
});
|
||||
it('errors on dot-starting extension', () => {
|
||||
|
||||
@@ -17,7 +17,8 @@ describe('OsSpecificTerminalLaunchCommandFactory', () => {
|
||||
[OperatingSystem.Linux]: LinuxVisibleTerminalCommand,
|
||||
[OperatingSystem.macOS]: MacOsVisibleTerminalCommand,
|
||||
};
|
||||
AllSupportedOperatingSystems.forEach((operatingSystem) => {
|
||||
AllSupportedOperatingSystems.forEach((operatingSystemValue) => {
|
||||
const operatingSystem = operatingSystemValue as SupportedOperatingSystem;
|
||||
it(`${OperatingSystem[operatingSystem]}`, () => {
|
||||
// arrange
|
||||
const expectedDefinitionType = testScenarios[operatingSystem];
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ViteEnvironmentVariables } from '@/infrastructure/EnvironmentVariables/
|
||||
|
||||
describe('ViteEnvironmentVariables', () => {
|
||||
describe('reads values from import.meta.env', () => {
|
||||
let originalMetaEnv;
|
||||
let originalMetaEnv: ImportMetaEnv;
|
||||
beforeEach(() => {
|
||||
originalMetaEnv = { ...import.meta.env };
|
||||
});
|
||||
@@ -16,14 +16,15 @@ describe('ViteEnvironmentVariables', () => {
|
||||
Object.assign(import.meta.env, originalMetaEnv);
|
||||
});
|
||||
|
||||
interface ITestCase<T> {
|
||||
interface EnvironmentVariableTestScenario<T> {
|
||||
readonly getActualValue: (sut: IEnvironmentVariables) => T;
|
||||
readonly environmentVariable: typeof VITE_ENVIRONMENT_KEYS[
|
||||
keyof typeof VITE_ENVIRONMENT_KEYS];
|
||||
readonly expected: T;
|
||||
}
|
||||
const testCases: {
|
||||
readonly [K in PropertyKeys<IEnvironmentVariables>]: ITestCase<string | boolean>;
|
||||
readonly [K in PropertyKeys<IEnvironmentVariables>]:
|
||||
EnvironmentVariableTestScenario<string | boolean>;
|
||||
} = {
|
||||
name: {
|
||||
environmentVariable: VITE_ENVIRONMENT_KEYS.NAME,
|
||||
|
||||
@@ -119,14 +119,15 @@ describe('ConditionBasedOsDetector', () => {
|
||||
});
|
||||
|
||||
describe('user agent checks', () => {
|
||||
const testScenarios: ReadonlyArray<{
|
||||
interface UserAgentTestScenario {
|
||||
readonly description: string;
|
||||
readonly buildEnvironment: (environment: BrowserEnvironmentStub) => BrowserEnvironmentStub;
|
||||
readonly buildCondition: (condition: BrowserConditionStub) => BrowserConditionStub;
|
||||
readonly detects: boolean;
|
||||
}> = [
|
||||
}
|
||||
const testScenarios: ReadonlyArray<UserAgentTestScenario> = [
|
||||
...getAbsentStringTestCases({ excludeUndefined: true, excludeNull: true })
|
||||
.map((testCase) => ({
|
||||
.map((testCase): UserAgentTestScenario => ({
|
||||
description: `does not detect when user agent is empty (${testCase.valueName})`,
|
||||
buildEnvironment: (environment) => environment.withUserAgent(testCase.absentValue),
|
||||
buildCondition: (condition) => condition,
|
||||
|
||||
@@ -77,7 +77,8 @@ describe('WindowVariablesValidator', () => {
|
||||
});
|
||||
|
||||
describe('does not throw when a property is valid', () => {
|
||||
const testScenarios: Record<PropertyKeys<Required<WindowVariables>>, ReadonlyArray<{
|
||||
type WindowVariable = PropertyKeys<Required<WindowVariables>>;
|
||||
const testScenarios: Record<WindowVariable, ReadonlyArray<{
|
||||
readonly description: string;
|
||||
readonly validValue: unknown;
|
||||
}>> = {
|
||||
@@ -117,8 +118,10 @@ describe('WindowVariablesValidator', () => {
|
||||
validValueScenarios.forEach(({ description, validValue }) => {
|
||||
it(description, () => {
|
||||
// arrange
|
||||
const input = new WindowVariablesStub();
|
||||
input[propertyKey] = validValue;
|
||||
const input: WindowVariables = {
|
||||
...new WindowVariablesStub(),
|
||||
[propertyKey]: validValue,
|
||||
};
|
||||
const context = new ValidateWindowVariablesTestSetup()
|
||||
.withWindowVariables(input);
|
||||
// act
|
||||
@@ -173,8 +176,10 @@ describe('WindowVariablesValidator', () => {
|
||||
name: propertyKey as keyof WindowVariables,
|
||||
value: invalidValue,
|
||||
});
|
||||
const input = new WindowVariablesStub();
|
||||
input[propertyKey] = invalidValue;
|
||||
const input: WindowVariables = {
|
||||
...new WindowVariablesStub(),
|
||||
[propertyKey]: invalidValue,
|
||||
};
|
||||
const context = new ValidateWindowVariablesTestSetup()
|
||||
.withWindowVariables(input);
|
||||
// act
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChecks';
|
||||
import type { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/ISanityCheckOptions';
|
||||
import type { SanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/SanityCheckOptions';
|
||||
import { SanityCheckOptionsStub } from '@tests/unit/shared/Stubs/SanityCheckOptionsStub';
|
||||
import type { ISanityValidator } from '@/infrastructure/RuntimeSanity/Common/ISanityValidator';
|
||||
import type { SanityValidator } from '@/infrastructure/RuntimeSanity/Common/SanityValidator';
|
||||
import { SanityValidatorStub } from '@tests/unit/shared/Stubs/SanityValidatorStub';
|
||||
import { itEachAbsentCollectionValue } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import { collectExceptionMessage } from '@tests/unit/shared/ExceptionCollector';
|
||||
@@ -11,7 +11,7 @@ describe('SanityChecks', () => {
|
||||
describe('validateRuntimeSanity', () => {
|
||||
describe('parameter validation', () => {
|
||||
describe('throws when validators are empty', () => {
|
||||
itEachAbsentCollectionValue<ISanityValidator>((absentCollection) => {
|
||||
itEachAbsentCollectionValue<SanityValidator>((absentCollection) => {
|
||||
// arrange
|
||||
const expectedError = 'missing validators';
|
||||
const validators = absentCollection;
|
||||
@@ -138,9 +138,9 @@ describe('SanityChecks', () => {
|
||||
});
|
||||
|
||||
class TestContext {
|
||||
private options: ISanityCheckOptions = new SanityCheckOptionsStub();
|
||||
private options: SanityCheckOptions = new SanityCheckOptionsStub();
|
||||
|
||||
private validators: ISanityValidator[] = [new SanityValidatorStub()];
|
||||
private validators: SanityValidator[] = [new SanityValidatorStub()];
|
||||
|
||||
public withOptionsSetup(
|
||||
setup: (stub: SanityCheckOptionsStub) => SanityCheckOptionsStub,
|
||||
@@ -148,12 +148,12 @@ class TestContext {
|
||||
return this.withOptions(setup(new SanityCheckOptionsStub()));
|
||||
}
|
||||
|
||||
public withOptions(options: ISanityCheckOptions): this {
|
||||
public withOptions(options: SanityCheckOptions): this {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withValidators(validators: ISanityValidator[]): this {
|
||||
public withValidators(validators: SanityValidator[]): this {
|
||||
this.validators = validators;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import type { PropertyKeys } from '@/TypeHelpers';
|
||||
import type { FactoryFunction, FactoryValidator } from '@/infrastructure/RuntimeSanity/Common/FactoryValidator';
|
||||
import type { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/ISanityCheckOptions';
|
||||
import type { SanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/SanityCheckOptions';
|
||||
import { SanityCheckOptionsStub } from '@tests/unit/shared/Stubs/SanityCheckOptionsStub';
|
||||
|
||||
interface ITestOptions<T> {
|
||||
createValidator: (factory?: FactoryFunction<T>) => FactoryValidator<T>;
|
||||
enablingOptionProperty: PropertyKeys<ISanityCheckOptions>;
|
||||
factoryFunctionStub: FactoryFunction<T>;
|
||||
expectedValidatorName: string;
|
||||
interface TestOptions<T> {
|
||||
readonly createValidator: (factory?: FactoryFunction<T>) => FactoryValidator<T>;
|
||||
readonly enablingOptionProperty: PropertyKeys<SanityCheckOptions>;
|
||||
readonly factoryFunctionStub: FactoryFunction<T>;
|
||||
readonly expectedValidatorName: string;
|
||||
}
|
||||
|
||||
export function runFactoryValidatorTests<T>(
|
||||
testOptions: ITestOptions<T>,
|
||||
testOptions: TestOptions<T>,
|
||||
) {
|
||||
describe('shouldValidate', () => {
|
||||
it('returns true when option is true', () => {
|
||||
// arrange
|
||||
const expectedValue = true;
|
||||
const options: ISanityCheckOptions = {
|
||||
const options: SanityCheckOptions = {
|
||||
...new SanityCheckOptionsStub(),
|
||||
[testOptions.enablingOptionProperty]: true,
|
||||
};
|
||||
@@ -31,7 +31,7 @@ export function runFactoryValidatorTests<T>(
|
||||
it('returns false when option is false', () => {
|
||||
// arrange
|
||||
const expectedValue = false;
|
||||
const options: ISanityCheckOptions = {
|
||||
const options: SanityCheckOptions = {
|
||||
...new SanityCheckOptionsStub(),
|
||||
[testOptions.enablingOptionProperty]: false,
|
||||
};
|
||||
|
||||
@@ -7,12 +7,14 @@ import { itIsSingletonFactory } from '@tests/unit/shared/TestCases/SingletonFact
|
||||
import type { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
import { itIsTransientFactory } from '@tests/unit/shared/TestCases/TransientFactoryTests';
|
||||
import { executeInComponentSetupContext } from '@tests/shared/Vue/ExecuteInComponentSetupContext';
|
||||
import type { PropertyKeys } from '@/TypeHelpers';
|
||||
|
||||
type InjectionKeyType = PropertyKeys<typeof InjectionKeys>;
|
||||
type DependencyInjectionTestFunction = (injectionKey: symbol) => void;
|
||||
|
||||
describe('DependencyProvider', () => {
|
||||
describe('provideDependencies', () => {
|
||||
const testCases: {
|
||||
readonly [K in keyof typeof InjectionKeys]: (injectionKey: symbol) => void;
|
||||
} = {
|
||||
const testCases: Record<InjectionKeyType, DependencyInjectionTestFunction> = {
|
||||
useCollectionState: createTransientTests(),
|
||||
useApplication: createSingletonTests(),
|
||||
useRuntimeEnvironment: createSingletonTests(),
|
||||
@@ -27,7 +29,8 @@ describe('DependencyProvider', () => {
|
||||
useAutoUnsubscribedEventListener: createTransientTests(),
|
||||
};
|
||||
Object.entries(testCases).forEach(([key, runTests]) => {
|
||||
const registeredKey = InjectionKeys[key].key;
|
||||
const injectionKey = key as InjectionKeyType;
|
||||
const registeredKey = InjectionKeys[injectionKey].key;
|
||||
describe(`Key: "${registeredKey.toString()}"`, () => {
|
||||
runTests(registeredKey);
|
||||
});
|
||||
@@ -35,7 +38,7 @@ describe('DependencyProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function createTransientTests() {
|
||||
function createTransientTests(): DependencyInjectionTestFunction {
|
||||
return (injectionKey: symbol) => {
|
||||
it('should register a function when transient dependency is resolved', () => {
|
||||
// arrange
|
||||
@@ -73,7 +76,7 @@ function createTransientTests() {
|
||||
};
|
||||
}
|
||||
|
||||
function createSingletonTests() {
|
||||
function createSingletonTests(): DependencyInjectionTestFunction {
|
||||
return (injectionKey: symbol) => {
|
||||
it('should register an object when singleton dependency is resolved', () => {
|
||||
// arrange
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/ISanityCheckOptions';
|
||||
import { RuntimeSanityValidator } from '@/presentation/bootstrapping/Modules/RuntimeSanityValidator';
|
||||
import type { SanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/SanityCheckOptions';
|
||||
import { RuntimeSanityBootstrapper } from '@/presentation/bootstrapping/Modules/RuntimeSanityBootstrapper';
|
||||
import { expectDoesNotThrowAsync, expectThrowsAsync } from '@tests/shared/Assertions/ExpectThrowsAsync';
|
||||
import type { RuntimeSanityValidator } from '@/infrastructure/RuntimeSanity/SanityChecks';
|
||||
|
||||
describe('RuntimeSanityValidator', () => {
|
||||
describe('RuntimeSanityBootstrapper', () => {
|
||||
it('calls validator with correct options upon bootstrap', async () => {
|
||||
// arrange
|
||||
const expectedOptions: ISanityCheckOptions = {
|
||||
const expectedOptions: SanityCheckOptions = {
|
||||
validateEnvironmentVariables: true,
|
||||
validateWindowVariables: true,
|
||||
};
|
||||
let actualOptions: ISanityCheckOptions | undefined;
|
||||
const validatorMock = (options) => {
|
||||
let actualOptions: SanityCheckOptions | undefined;
|
||||
const validatorMock: RuntimeSanityValidator = (options) => {
|
||||
actualOptions = options;
|
||||
};
|
||||
const sut = new RuntimeSanityValidator(validatorMock);
|
||||
const sut = new RuntimeSanityBootstrapper(validatorMock);
|
||||
// act
|
||||
await sut.bootstrap();
|
||||
// assert
|
||||
@@ -26,7 +27,7 @@ describe('RuntimeSanityValidator', () => {
|
||||
const validatorMock = () => {
|
||||
throw new Error(expectedMessage);
|
||||
};
|
||||
const sut = new RuntimeSanityValidator(validatorMock);
|
||||
const sut = new RuntimeSanityBootstrapper(validatorMock);
|
||||
// act
|
||||
const act = async () => { await sut.bootstrap(); };
|
||||
// assert
|
||||
@@ -35,7 +36,7 @@ describe('RuntimeSanityValidator', () => {
|
||||
it('runs successfully if validator passes', async () => {
|
||||
// arrange
|
||||
const validatorMock = () => { /* NOOP */ };
|
||||
const sut = new RuntimeSanityValidator(validatorMock);
|
||||
const sut = new RuntimeSanityBootstrapper(validatorMock);
|
||||
// act
|
||||
const act = async () => { await sut.bootstrap(); };
|
||||
// assert
|
||||
@@ -17,7 +17,8 @@ describe('PlatformInstructionSteps', () => {
|
||||
[OperatingSystem.macOS]: MacOsInstructions,
|
||||
[OperatingSystem.Linux]: LinuxInstructions,
|
||||
};
|
||||
AllSupportedOperatingSystems.forEach((operatingSystem) => {
|
||||
AllSupportedOperatingSystems.forEach((operatingSystemKey) => {
|
||||
const operatingSystem = operatingSystemKey as SupportedOperatingSystem;
|
||||
it(`renders the correct component for ${OperatingSystem[operatingSystem]}`, () => {
|
||||
// arrange
|
||||
const expectedComponent = testScenarios[operatingSystem];
|
||||
@@ -47,7 +48,9 @@ describe('PlatformInstructionSteps', () => {
|
||||
|
||||
// assert
|
||||
const componentWrapper = wrapper.findComponent(wrappedComponent);
|
||||
expect(componentWrapper.props('filename')).to.equal(expectedFilename);
|
||||
const propertyValues = componentWrapper.props();
|
||||
const propertyValue = 'filename' in propertyValues ? propertyValues.filename : undefined;
|
||||
expect(propertyValue).to.equal(expectedFilename);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('CompositeMarkdownRenderer', () => {
|
||||
it('throws error without renderers', () => {
|
||||
// arrange
|
||||
const expectedError = 'missing renderers';
|
||||
const renderers = [];
|
||||
const renderers = new Array<MarkdownRenderer>();
|
||||
const context = new MarkdownRendererTestBuilder()
|
||||
.withMarkdownRenderers(renderers);
|
||||
// act
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('TreeNodeHierarchy', () => {
|
||||
it('returns `true` without children', () => {
|
||||
// arrange
|
||||
const hierarchy = new TreeNodeHierarchy();
|
||||
const children = [];
|
||||
const children = new Array<TreeNode>();
|
||||
// act
|
||||
hierarchy.setChildren(children);
|
||||
// assert
|
||||
@@ -55,7 +55,7 @@ describe('TreeNodeHierarchy', () => {
|
||||
it('returns `false` without children', () => {
|
||||
// arrange
|
||||
const hierarchy = new TreeNodeHierarchy();
|
||||
const children = [];
|
||||
const children = new Array<TreeNode>();
|
||||
// act
|
||||
hierarchy.setChildren(children);
|
||||
// assert
|
||||
|
||||
@@ -237,7 +237,7 @@ describe('useGradualNodeRendering', () => {
|
||||
});
|
||||
it('skips scheduling when no nodes to render', () => {
|
||||
// arrange
|
||||
const nodes = [];
|
||||
const nodes = new Array<TreeNode>();
|
||||
const nodesStub = new UseCurrentTreeNodesStub()
|
||||
.withQueryableNodes(new QueryableNodesStub().withFlattenedNodes(nodes));
|
||||
const delaySchedulerStub = new DelaySchedulerStub();
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('parseTreeInput', () => {
|
||||
|
||||
it('returns an empty array if given an empty array', () => {
|
||||
// arrange
|
||||
const input = [];
|
||||
const input = new Array<TreeInputNodeData>();
|
||||
// act
|
||||
const nodes = parseTreeInput(input);
|
||||
// assert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SingleNodeCollectionFocusManager } from '@/presentation/components/Scripts/View/Tree/TreeView/TreeRoot/Focus/SingleNodeCollectionFocusManager';
|
||||
import type { TreeNodeCollection } from '@/presentation/components/Scripts/View/Tree/TreeView/TreeRoot/NodeCollection/TreeNodeCollection';
|
||||
import { TreeNodeInitializerAndUpdater } from '@/presentation/components/Scripts/View/Tree/TreeView/TreeRoot/NodeCollection/TreeNodeInitializerAndUpdater';
|
||||
import { TreeRootManager } from '@/presentation/components/Scripts/View/Tree/TreeView/TreeRoot/TreeRootManager';
|
||||
import { TreeRootManager, type FocusManagerFactory } from '@/presentation/components/Scripts/View/Tree/TreeView/TreeRoot/TreeRootManager';
|
||||
import { SingleNodeFocusManagerStub } from '@tests/unit/shared/Stubs/SingleNodeFocusManagerStub';
|
||||
import { TreeNodeCollectionStub } from '@tests/unit/shared/Stubs/TreeNodeCollectionStub';
|
||||
|
||||
@@ -19,9 +19,12 @@ describe('TreeRootManager', () => {
|
||||
it('set by constructor as expected', () => {
|
||||
// arrange
|
||||
const expectedCollection = new TreeNodeCollectionStub();
|
||||
const sut = new TreeRootManager();
|
||||
const context = new TestContext()
|
||||
.withNodeCollection(expectedCollection);
|
||||
// act
|
||||
const actualCollection = sut.collection;
|
||||
const actualCollection = context
|
||||
.build()
|
||||
.collection;
|
||||
// assert
|
||||
expect(actualCollection).to.equal(expectedCollection);
|
||||
});
|
||||
@@ -39,15 +42,41 @@ describe('TreeRootManager', () => {
|
||||
it('creates with same collection it uses', () => {
|
||||
// arrange
|
||||
let usedCollection: TreeNodeCollection | undefined;
|
||||
const factoryMock = (collection) => {
|
||||
const factoryMock: FocusManagerFactory = (collection) => {
|
||||
usedCollection = collection;
|
||||
return new SingleNodeFocusManagerStub();
|
||||
};
|
||||
const sut = new TreeRootManager(new TreeNodeCollectionStub(), factoryMock);
|
||||
const context = new TestContext()
|
||||
.withFocusManagerFactory(factoryMock);
|
||||
// act
|
||||
const expected = sut.collection;
|
||||
const expected = context
|
||||
.build()
|
||||
.collection;
|
||||
// assert
|
||||
expect(usedCollection).to.equal(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestContext {
|
||||
private nodeCollection: TreeNodeCollection = new TreeNodeCollectionStub();
|
||||
|
||||
private focusManagerFactory: FocusManagerFactory = () => new SingleNodeFocusManagerStub();
|
||||
|
||||
public withFocusManagerFactory(focusManagerFactory: FocusManagerFactory): this {
|
||||
this.focusManagerFactory = focusManagerFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withNodeCollection(nodeCollection: TreeNodeCollection): this {
|
||||
this.nodeCollection = nodeCollection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public build(): TreeRootManager {
|
||||
return new TreeRootManager(
|
||||
this.nodeCollection,
|
||||
this.focusManagerFactory,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { UseUserSelectionStateStub } from '@tests/unit/shared/Stubs/UseUserSelec
|
||||
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
||||
import type { TreeNodeId } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
|
||||
import type { Executable } from '@/domain/Executables/Executable';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
|
||||
describe('useSelectedScriptNodeIds', () => {
|
||||
it('returns an empty array when no scripts are selected', () => {
|
||||
@@ -44,7 +45,7 @@ describe('useSelectedScriptNodeIds', () => {
|
||||
});
|
||||
it('when the selection state changes', () => {
|
||||
// arrange
|
||||
const initialScripts = [];
|
||||
const initialScripts = new Array<SelectedScript>();
|
||||
const changedScripts = [
|
||||
new SelectedScriptStub(new ScriptStub('id-1')),
|
||||
new SelectedScriptStub(new ScriptStub('id-2')),
|
||||
|
||||
@@ -67,7 +67,7 @@ function runSharedTestsForAnimation(
|
||||
};
|
||||
const element = document.createElement('div');
|
||||
Object.entries(expectedStyleValues).forEach(([key, value]) => {
|
||||
element.style[key] = value;
|
||||
element.style[key as keyof MutatedStyleProperties] = value;
|
||||
});
|
||||
const timer = new TimerStub();
|
||||
const hookResult = useExpandCollapseAnimation(timer);
|
||||
@@ -78,7 +78,8 @@ function runSharedTestsForAnimation(
|
||||
await promise;
|
||||
// assert
|
||||
Object.entries(expectedStyleValues).forEach(([key, expectedStyleValue]) => {
|
||||
const actualStyleValue = element.style[key];
|
||||
const styleProperty = key as keyof MutatedStyleProperties;
|
||||
const actualStyleValue = element.style[styleProperty];
|
||||
expect(actualStyleValue).to.equal(expectedStyleValue, formatAssertionMessage([
|
||||
`Style key: ${key}`,
|
||||
`Expected style value: ${expectedStyleValue}`,
|
||||
@@ -86,7 +87,7 @@ function runSharedTestsForAnimation(
|
||||
`Initial style value: ${expectedStyleValues}`,
|
||||
'All styles:',
|
||||
...Object.entries(expectedStyleValues)
|
||||
.map(([k, value]) => indentText(`- ${k} > actual: "${element.style[k]}" | expected: "${value}"`)),
|
||||
.map(([k, value]) => indentText(`- ${k} > actual: "${actualStyleValue}" | expected: "${value}"`)),
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,18 +30,19 @@ describe('useClipboard', () => {
|
||||
} = {
|
||||
copyText: ['text-arg'],
|
||||
};
|
||||
Object.entries(testScenarios).forEach(([functionName, testFunctionArgs]) => {
|
||||
describe(functionName, () => {
|
||||
Object.entries(testScenarios).forEach(([functionNameValue, testFunctionArgs]) => {
|
||||
const functionName = functionNameValue as ClipboardFunction;
|
||||
describe(functionNameValue, () => {
|
||||
it('binds the method to the instance', () => {
|
||||
// arrange
|
||||
const expectedArgs = testFunctionArgs;
|
||||
const clipboardStub = new ClipboardStub();
|
||||
// act
|
||||
const clipboard = useClipboard(clipboardStub);
|
||||
const { [functionName as ClipboardFunction]: testFunction } = clipboard;
|
||||
const { [functionName]: testFunction } = clipboard;
|
||||
// assert
|
||||
testFunction(...expectedArgs);
|
||||
const call = clipboardStub.callHistory.find((c) => c.methodName === functionName);
|
||||
const call = clipboardStub.callHistory.find((c) => c.methodName === functionNameValue);
|
||||
expectExists(call);
|
||||
expect(call.args).to.deep.equal(expectedArgs);
|
||||
});
|
||||
@@ -50,14 +51,15 @@ describe('useClipboard', () => {
|
||||
const clipboardStub = new ClipboardStub();
|
||||
const expectedThisContext = clipboardStub;
|
||||
let actualThisContext: typeof expectedThisContext | undefined;
|
||||
// eslint-disable-next-line func-names
|
||||
clipboardStub[functionName] = function () {
|
||||
// eslint-disable-next-line func-names, @typescript-eslint/no-unused-vars
|
||||
clipboardStub[functionName] = function (_text) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
actualThisContext = this;
|
||||
return Promise.resolve();
|
||||
};
|
||||
// act
|
||||
const clipboard = useClipboard(clipboardStub);
|
||||
const { [functionName as ClipboardFunction]: testFunction } = clipboard;
|
||||
const { [functionNameValue as ClipboardFunction]: testFunction } = clipboard;
|
||||
// assert
|
||||
testFunction(...testFunctionArgs);
|
||||
expect(expectedThisContext).to.equal(actualThisContext);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
||||
import { CategoryCollectionStateStub } from '@tests/unit/shared/Stubs/CategoryCollectionStateStub';
|
||||
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
|
||||
import { ScriptSelectionStub } from '@tests/unit/shared/Stubs/ScriptSelectionStub';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
|
||||
describe('useUserSelectionState', () => {
|
||||
describe('currentSelection', () => {
|
||||
@@ -170,8 +171,8 @@ describe('useUserSelectionState', () => {
|
||||
describe('triggers change', () => {
|
||||
it('with new selected scripts array reference', async () => {
|
||||
// arrange
|
||||
const oldSelectedScriptsArrayReference = [];
|
||||
const newSelectedScriptsArrayReference = [];
|
||||
const oldSelectedScriptsArrayReference = new Array<SelectedScript>();
|
||||
const newSelectedScriptsArrayReference = new Array<SelectedScript>();
|
||||
const scriptSelectionStub = new ScriptSelectionStub()
|
||||
.withSelectedScripts(oldSelectedScriptsArrayReference);
|
||||
const collectionStateStub = new UseCollectionStateStub()
|
||||
@@ -191,7 +192,7 @@ describe('useUserSelectionState', () => {
|
||||
});
|
||||
it('with same selected scripts array reference', async () => {
|
||||
// arrange
|
||||
const sharedSelectedScriptsReference = [];
|
||||
const sharedSelectedScriptsReference = new Array<SelectedScript>();
|
||||
const scriptSelectionStub = new ScriptSelectionStub()
|
||||
.withSelectedScripts(sharedSelectedScriptsReference);
|
||||
const collectionStateStub = new UseCollectionStateStub()
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('IpcRegistration', () => {
|
||||
// act
|
||||
context.run();
|
||||
// assert
|
||||
const channel = IpcChannelDefinitions[key];
|
||||
const channel = IpcChannelDefinitions[key as ChannelDefinitionKey] as IpcChannel<unknown>;
|
||||
const actualInstance = getRegisteredInstance(channel);
|
||||
expect(actualInstance).to.equal(expectedInstance);
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('RendererApiProvider', () => {
|
||||
// act
|
||||
const variables = testContext.provideWindowVariables();
|
||||
// assert
|
||||
const actualValue = variables[property];
|
||||
const actualValue = variables[property as PropertyKeys<Required<WindowVariables>>];
|
||||
expect(actualValue).to.equal(expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type { IpcChannel } from '@/presentation/electron/shared/IpcBridging/IpcChannel';
|
||||
import { type ChannelDefinitionKey, IpcChannelDefinitions } from '@/presentation/electron/shared/IpcBridging/IpcChannelDefinitions';
|
||||
|
||||
describe('IpcChannelDefinitions', () => {
|
||||
@@ -25,7 +24,7 @@ describe('IpcChannelDefinitions', () => {
|
||||
[definitionKey, { expectedNamespace, expectedAccessibleMembers }],
|
||||
) => {
|
||||
describe(`channel: "${definitionKey}"`, () => {
|
||||
const ipcChannelUnderTest = IpcChannelDefinitions[definitionKey] as IpcChannel<unknown>;
|
||||
const ipcChannelUnderTest = IpcChannelDefinitions[definitionKey as ChannelDefinitionKey];
|
||||
it('has expected namespace', () => {
|
||||
// act
|
||||
const actualNamespace = ipcChannelUnderTest.namespace;
|
||||
|
||||
@@ -38,7 +38,7 @@ export class ExpressionStub implements IExpression {
|
||||
this.callHistory.push(context);
|
||||
if (this.result === undefined /* not empty string */) {
|
||||
const { args } = context;
|
||||
return `[expression-stub] args: ${args ? Object.keys(args).map((key) => `${key}: ${args[key]}`).join('", "') : 'none'}`;
|
||||
return `[expression-stub] args: ${args ? Object.entries(args).map((key, value) => `${key}: ${value}`).join('", "') : 'none'}`;
|
||||
}
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/ISanityCheckOptions';
|
||||
import type { SanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/SanityCheckOptions';
|
||||
|
||||
export class SanityCheckOptionsStub implements ISanityCheckOptions {
|
||||
export class SanityCheckOptionsStub implements SanityCheckOptions {
|
||||
public validateWindowVariables = false;
|
||||
|
||||
public validateEnvironmentVariables = false;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/ISanityCheckOptions';
|
||||
import type { ISanityValidator } from '@/infrastructure/RuntimeSanity/Common/ISanityValidator';
|
||||
import type { SanityCheckOptions } from '@/infrastructure/RuntimeSanity/Common/SanityCheckOptions';
|
||||
import type { SanityValidator } from '@/infrastructure/RuntimeSanity/Common/SanityValidator';
|
||||
|
||||
export class SanityValidatorStub implements ISanityValidator {
|
||||
public shouldValidateArgs = new Array<ISanityCheckOptions>();
|
||||
export class SanityValidatorStub implements SanityValidator {
|
||||
public shouldValidateArgs = new Array<SanityCheckOptions>();
|
||||
|
||||
public name = 'sanity-validator-stub';
|
||||
|
||||
@@ -10,7 +10,7 @@ export class SanityValidatorStub implements ISanityValidator {
|
||||
|
||||
private shouldValidateResult = true;
|
||||
|
||||
public shouldValidate(options: ISanityCheckOptions): boolean {
|
||||
public shouldValidate(options: SanityCheckOptions): boolean {
|
||||
this.shouldValidateArgs.push(options);
|
||||
return this.shouldValidateResult;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class VueDependencyInjectionApiStub implements VueDependencyInjectionApi
|
||||
public inject<T>(key: InjectionKey<T>): T {
|
||||
const providedValue = this.injections.get(key);
|
||||
if (providedValue === undefined) {
|
||||
throw new Error(`[VueDependencyInjectionApiStub] No value provided for key: ${String(key)}`);
|
||||
throw new Error(`[${VueDependencyInjectionApiStub.name}] No value provided for key: ${String(key)}`);
|
||||
}
|
||||
return providedValue as T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user