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:
undergroundwires
2023-11-12 22:54:00 +01:00
parent 7ab16ecccb
commit 949fac1a7c
294 changed files with 2477 additions and 2738 deletions

View File

@@ -6,8 +6,7 @@ import { IApplicationContext, IApplicationContextChangedEvent } from '@/applicat
import { IApplication } from '@/domain/IApplication';
import { ApplicationStub } from '@tests/unit/shared/Stubs/ApplicationStub';
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner';
import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
describe('ApplicationContext', () => {
describe('changeContext', () => {
@@ -51,6 +50,19 @@ describe('ApplicationContext', () => {
// assert
expectEmptyState(sut.state);
});
it('throws when OS is unknown to application', () => {
// arrange
const expectedError = 'expected error from application';
const applicationStub = new ApplicationStub();
const sut = new ObservableApplicationContextFactory()
.withApp(applicationStub)
.construct();
// act
applicationStub.getCollection = () => { throw new Error(expectedError); };
const act = () => sut.changeContext(OperatingSystem.Android);
// assert
expect(act).to.throw(expectedError);
});
});
it('remembers old state when changed backed to same os', () => {
// arrange
@@ -70,6 +82,7 @@ describe('ApplicationContext', () => {
sut.state.filter.applyFilter('second-state');
sut.changeContext(os);
// assert
expectExists(sut.state.filter.currentFilter);
const actualFilter = sut.state.filter.currentFilter.query;
expect(actualFilter).to.equal(expectedFilter);
});
@@ -125,35 +138,8 @@ describe('ApplicationContext', () => {
expect(duplicates.length).to.be.equal(0);
});
});
describe('throws with invalid os', () => {
new EnumRangeTestRunner((os: OperatingSystem) => {
// arrange
const sut = new ObservableApplicationContextFactory()
.construct();
// act
sut.changeContext(os);
})
// assert
.testOutOfRangeThrows()
.testAbsentValueThrows()
.testInvalidValueThrows(OperatingSystem.Android, 'os "Android" is not defined in application');
});
});
describe('ctor', () => {
describe('app', () => {
describe('throw when app is missing', () => {
itEachAbsentObjectValue((absentValue) => {
// arrange
const expectedError = 'missing app';
const app = absentValue;
const os = OperatingSystem.Windows;
// act
const act = () => new ApplicationContext(app, os);
// assert
expect(act).to.throw(expectedError);
});
});
});
describe('collection', () => {
it('returns right collection for expected OS', () => {
// arrange
@@ -195,16 +181,17 @@ describe('ApplicationContext', () => {
const actual = sut.state.os;
expect(actual).to.deep.equal(expected);
});
describe('throws when OS is invalid', () => {
it('throws when OS is unknown to application', () => {
// arrange
const expectedError = 'expected error from application';
const applicationStub = new ApplicationStub();
applicationStub.getCollection = () => { throw new Error(expectedError); };
// act
const act = (os: OperatingSystem) => new ObservableApplicationContextFactory()
.withInitialOs(os)
const act = () => new ObservableApplicationContextFactory()
.withApp(applicationStub)
.construct();
// assert
new EnumRangeTestRunner(act)
.testOutOfRangeThrows()
.testAbsentValueThrows()
.testInvalidValueThrows(OperatingSystem.Android, 'os "Android" is not defined in application');
expect(act).to.throw(expectedError);
});
});
describe('app', () => {

View File

@@ -7,7 +7,6 @@ import { IApplication } from '@/domain/IApplication';
import { RuntimeEnvironmentStub } from '@tests/unit/shared/Stubs/RuntimeEnvironmentStub';
import { ApplicationStub } from '@tests/unit/shared/Stubs/ApplicationStub';
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
import { expectThrowsAsync } from '@tests/shared/Assertions/ExpectThrowsAsync';
describe('ApplicationContextFactory', () => {
describe('buildContext', () => {
@@ -23,19 +22,10 @@ describe('ApplicationContextFactory', () => {
// assert
expect(expected).to.equal(context.app);
});
it('throws when null', async () => {
// arrange
const expectedError = 'missing factory';
const factory = null;
// act
const act = async () => { await buildContext(factory); };
// assert
expectThrowsAsync(act, expectedError);
});
});
describe('environment', () => {
describe('sets initial OS as expected', () => {
it('returns currentOs if it is supported', async () => {
it('returns current OS if it is supported', async () => {
// arrange
const expected = OperatingSystem.Windows;
const environment = new RuntimeEnvironmentStub().withOs(expected);
@@ -47,7 +37,7 @@ describe('ApplicationContextFactory', () => {
const actual = context.state.os;
expect(expected).to.equal(actual);
});
it('fallbacks to other os if OS in environment is not supported', async () => {
it('fallbacks to other OS if OS in environment is not supported', async () => {
// arrange
const expected = OperatingSystem.Windows;
const currentOs = OperatingSystem.macOS;
@@ -60,15 +50,34 @@ describe('ApplicationContextFactory', () => {
const actual = context.state.os;
expect(expected).to.equal(actual);
});
it('fallbacks to most supported os if current os is not supported', async () => {
it('fallbacks to most supported OS if current OS is not supported', async () => {
// arrange
const runtimeOs = OperatingSystem.macOS;
const expectedOs = OperatingSystem.Android;
const allCollections = [
new CategoryCollectionStub().withOs(OperatingSystem.Linux).withTotalScripts(3),
new CategoryCollectionStub().withOs(expectedOs).withTotalScripts(5),
new CategoryCollectionStub().withOs(OperatingSystem.Windows).withTotalScripts(4),
];
const environment = new RuntimeEnvironmentStub().withOs(OperatingSystem.macOS);
const environment = new RuntimeEnvironmentStub().withOs(runtimeOs);
const app = new ApplicationStub().withCollections(...allCollections);
const factoryMock = mockFactoryWithApp(app);
// act
const context = await buildContext(factoryMock, environment);
// assert
const actual = context.state.os;
expect(expectedOs).to.equal(actual, `Expected: ${OperatingSystem[expectedOs]}, actual: ${OperatingSystem[actual]}`);
});
it('fallbacks to most supported OS if current OS is undefined', async () => {
// arrange
const runtimeOs = OperatingSystem.macOS;
const expectedOs = OperatingSystem.Android;
const allCollections = [
new CategoryCollectionStub().withOs(OperatingSystem.Linux).withTotalScripts(3),
new CategoryCollectionStub().withOs(expectedOs).withTotalScripts(5),
new CategoryCollectionStub().withOs(OperatingSystem.Windows).withTotalScripts(4),
];
const environment = new RuntimeEnvironmentStub().withOs(runtimeOs);
const app = new ApplicationStub().withCollections(...allCollections);
const factoryMock = mockFactoryWithApp(app);
// act
@@ -77,16 +86,6 @@ describe('ApplicationContextFactory', () => {
const actual = context.state.os;
expect(expectedOs).to.equal(actual, `Expected: ${OperatingSystem[expectedOs]}, actual: ${OperatingSystem[actual]}`);
});
});
it('throws when null', async () => {
// arrange
const expectedError = 'missing environment';
const factory = mockFactoryWithApp(undefined);
const environment = null;
// act
const act = async () => { await buildContext(factory, environment); };
// assert
expectThrowsAsync(act, expectedError);
});
});
});

View File

@@ -92,7 +92,11 @@ describe('CategoryCollectionState', () => {
// act
let actualScript: IScript | undefined;
sut.filter.filterChanged.on((result) => {
[actualScript] = result.filter?.scriptMatches ?? [undefined];
result.visit({
onApply: (filter) => {
[actualScript] = filter.scriptMatches;
},
});
});
sut.filter.applyFilter(scriptNameFilter);
// assert

View File

@@ -12,8 +12,7 @@ import { ScriptingDefinitionStub } from '@tests/unit/shared/Stubs/ScriptingDefin
import { CategoryStub } from '@tests/unit/shared/Stubs/CategoryStub';
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests';
import { UserSelectionStub } from '@tests/unit/shared/Stubs/UserSelectionStub';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
describe('ApplicationCode', () => {
describe('ctor', () => {
@@ -47,47 +46,12 @@ describe('ApplicationCode', () => {
// assert
expect(actual).to.equal(expected.code);
});
describe('throws when userSelection is missing', () => {
itEachAbsentObjectValue((absentValue) => {
// arrange
const expectedError = 'missing userSelection';
const userSelection = absentValue;
const definition = new ScriptingDefinitionStub();
// act
const act = () => new ApplicationCode(userSelection, definition);
// assert
expect(act).to.throw(expectedError);
});
});
describe('throws when scriptingDefinition is missing', () => {
itEachAbsentObjectValue((absentValue) => {
// arrange
const expectedError = 'missing scriptingDefinition';
const userSelection = new UserSelectionStub([]);
const definition = absentValue;
// act
const act = () => new ApplicationCode(userSelection, definition);
// assert
expect(act).to.throw(expectedError);
});
});
it('throws when generator is missing', () => {
// arrange
const expectedError = 'missing generator';
const userSelection = new UserSelectionStub([]);
const definition = new ScriptingDefinitionStub();
const generator = null;
// act
const act = () => new ApplicationCode(userSelection, definition, generator);
// assert
expect(act).to.throw(expectedError);
});
});
describe('changed event', () => {
describe('code', () => {
it('empty when nothing is selected', () => {
// arrange
let signaled: ICodeChangedEvent;
let signaled: ICodeChangedEvent | undefined;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
@@ -101,12 +65,13 @@ describe('ApplicationCode', () => {
// act
selection.changed.notify([]);
// assert
expectExists(signaled);
expect(signaled.code).to.have.lengthOf(0);
expect(signaled.code).to.equal(sut.current);
});
it('has code when some are selected', () => {
// arrange
let signaled: ICodeChangedEvent;
let signaled: ICodeChangedEvent | undefined;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
@@ -120,6 +85,7 @@ describe('ApplicationCode', () => {
// act
selection.changed.notify(scripts.map((s) => new SelectedScript(s, false)));
// assert
expectExists(signaled);
expect(signaled.code).to.have.length.greaterThan(0);
expect(signaled.code).to.equal(sut.current);
});
@@ -131,12 +97,12 @@ describe('ApplicationCode', () => {
const collection = new CategoryCollectionStub();
const selection = new UserSelection(collection, []);
const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts, definition) => {
buildCode: (_, definition) => {
if (definition !== expectedDefinition) {
throw new Error('Unexpected scripting definition');
}
return {
code: '',
code: 'non-important-code',
scriptPositions: new Map<SelectedScript, ICodePosition>(),
};
},
@@ -176,7 +142,7 @@ describe('ApplicationCode', () => {
});
it('sets positions from the generator', () => {
// arrange
let signaled: ICodeChangedEvent;
let signaled: ICodeChangedEvent | undefined;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
@@ -205,6 +171,7 @@ describe('ApplicationCode', () => {
// act
selection.changed.notify(scriptsToSelect);
// assert
expectExists(signaled);
expect(signaled.getScriptPositionInCode(scripts[0]))
.to.deep.equal(expected.get(scriptsToSelect[0]));
expect(signaled.getScriptPositionInCode(scripts[1]))

View File

@@ -166,6 +166,17 @@ describe('CodeChangedEvent', () => {
});
});
describe('getScriptPositionInCode', () => {
it('throws if script is unknown', () => {
// arrange
const expectedError = 'Unknown script: Position could not be found for the script';
const unknownScript = new ScriptStub('1');
const sut = new CodeChangedEventBuilder()
.build();
// act
const act = () => sut.getScriptPositionInCode(unknownScript);
// assert
expect(act).to.throw(expectedError);
});
it('returns expected position for existing script', () => {
// arrange
const script = new ScriptStub('1');

View File

@@ -5,8 +5,8 @@ import { ICodeBuilderFactory } from '@/application/Context/State/Code/Generation
import { ICodeBuilder } from '@/application/Context/State/Code/Generation/ICodeBuilder';
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
import { ScriptingDefinitionStub } from '@tests/unit/shared/Stubs/ScriptingDefinitionStub';
import { itEachAbsentObjectValue, itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests';
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
import { itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
describe('UserScriptGenerator', () => {
describe('scriptingDefinition', () => {
@@ -45,7 +45,7 @@ describe('UserScriptGenerator', () => {
// assert
const actual = code.code;
expect(actual.startsWith(expectedStart));
});
}, { excludeNull: true, excludeUndefined: true });
});
});
describe('endCode', () => {
@@ -83,54 +83,62 @@ describe('UserScriptGenerator', () => {
// assert
const actual = code.code;
expect(actual.endsWith(expectedEnd));
});
}, { excludeNull: true, excludeUndefined: true });
});
});
describe('throws when absent', () => {
itEachAbsentObjectValue((absentValue) => {
});
describe('execute', () => {
it('appends non-revert script', () => {
const sut = new UserScriptGenerator();
// arrange
const scriptName = 'test non-revert script';
const scriptCode = 'REM nop';
const script = new ScriptStub('id').withName(scriptName).withCode(scriptCode);
const selectedScripts = [new SelectedScript(script, false)];
const definition = new ScriptingDefinitionStub();
// act
const actual = sut.buildCode(selectedScripts, definition);
// assert
expect(actual.code).to.include(scriptName);
expect(actual.code).to.not.include(`${scriptName} (revert)`);
expect(actual.code).to.include(scriptCode);
});
});
describe('revert', () => {
it('appends revert script', () => {
// arrange
const sut = new UserScriptGenerator();
const scriptName = 'test non-revert script';
const scriptCode = 'REM nop';
const script = new ScriptStub('id')
.withName(scriptName)
.withRevertCode(scriptCode)
.toSelectedScript(true);
const definition = new ScriptingDefinitionStub();
// act
const actual = sut.buildCode([script], definition);
// assert
expect(actual.code).to.include(`${scriptName} (revert)`);
expect(actual.code).to.include(scriptCode);
});
describe('throws if revert script lacks revert code', () => {
itEachAbsentStringValue((emptyRevertCode) => {
// arrange
const expectedError = 'missing definition';
const expectedError = 'Reverted script lacks revert code.';
const sut = new UserScriptGenerator();
const scriptingDefinition = absentValue;
const selectedScripts = [new SelectedScriptStub('a')];
const script = new ScriptStub('id')
.toSelectedScript(true);
// Hack until SelectedScript is interface:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(script.script.code as any).revert = emptyRevertCode;
const definition = new ScriptingDefinitionStub();
// act
const act = () => sut.buildCode(selectedScripts, scriptingDefinition);
const act = () => sut.buildCode([script], definition);
// assert
expect(act).to.throw(expectedError);
});
}, { excludeNull: true });
});
});
it('appends revert script', () => {
// arrange
const sut = new UserScriptGenerator();
const scriptName = 'test non-revert script';
const scriptCode = 'REM nop';
const script = new ScriptStub('id')
.withName(scriptName)
.withRevertCode(scriptCode)
.toSelectedScript(true);
const definition = new ScriptingDefinitionStub();
// act
const actual = sut.buildCode([script], definition);
// assert
expect(actual.code).to.include(`${scriptName} (revert)`);
expect(actual.code).to.include(scriptCode);
});
it('appends non-revert script', () => {
const sut = new UserScriptGenerator();
// arrange
const scriptName = 'test non-revert script';
const scriptCode = 'REM nop';
const script = new ScriptStub('id').withName(scriptName).withCode(scriptCode);
const selectedScripts = [new SelectedScript(script, false)];
const definition = new ScriptingDefinitionStub();
// act
const actual = sut.buildCode(selectedScripts, definition);
// assert
expect(actual.code).to.include(scriptName);
expect(actual.code).to.not.include(`${scriptName} (revert)`);
expect(actual.code).to.include(scriptCode);
});
describe('scriptPositions', () => {
it('without script; returns empty', () => {
// arrange
@@ -179,6 +187,7 @@ describe('UserScriptGenerator', () => {
// expect
expect(1).to.equal(actual.scriptPositions.size);
const position = actual.scriptPositions.get(selectedScript);
expectExists(position);
expect(expectedStartLine).to.equal(position.startLine, 'Unexpected start line position');
expect(expectedEndLine).to.equal(position.endLine, 'Unexpected end line position');
});
@@ -209,28 +218,15 @@ describe('UserScriptGenerator', () => {
const firstPosition = actual.scriptPositions.get(selectedScripts[0]);
const secondPosition = actual.scriptPositions.get(selectedScripts[1]);
expect(actual.scriptPositions.size).to.equal(2);
expectExists(firstPosition);
expect(expectedFirstScriptStart).to.equal(firstPosition.startLine, 'Unexpected start line position (first script)');
expect(expectedFirstScriptEnd).to.equal(firstPosition.endLine, 'Unexpected end line position (first script)');
expectExists(secondPosition);
expect(expectedSecondScriptStart).to.equal(secondPosition.startLine, 'Unexpected start line position (second script)');
expect(expectedSecondScriptEnd).to.equal(secondPosition.endLine, 'Unexpected end line position (second script)');
});
});
});
describe('selectedScripts', () => {
describe('throws when absent', () => {
itEachAbsentObjectValue((absentValue) => {
// arrange
const expectedError = 'missing scripts';
const sut = new UserScriptGenerator();
const scriptingDefinition = new ScriptingDefinitionStub();
const selectedScripts = absentValue;
// act
const act = () => sut.buildCode(selectedScripts, scriptingDefinition);
// assert
expect(act).to.throw(expectedError);
});
});
});
});
function mockCodeBuilderFactory(mock: ICodeBuilder): ICodeBuilderFactory {

View File

@@ -1,30 +1,19 @@
import { describe, it, expect } from 'vitest';
import { FilterChange } from '@/application/Context/State/Filter/Event/FilterChange';
import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests';
import { FilterResultStub } from '@tests/unit/shared/Stubs/FilterResultStub';
import { FilterActionType } from '@/application/Context/State/Filter/Event/FilterActionType';
import { FilterChangeDetailsVisitorStub } from '@tests/unit/shared/Stubs/FilterChangeDetailsVisitorStub';
import { ApplyFilterAction } from '@/application/Context/State/Filter/Event/IFilterChangeDetails';
describe('FilterChange', () => {
describe('forApply', () => {
describe('throws when filter is absent', () => {
itEachAbsentObjectValue((absentValue) => {
// arrange
const expectedError = 'missing filter';
const filterValue = absentValue;
// act
const act = () => FilterChange.forApply(filterValue);
// assert
expect(act).to.throw(expectedError);
});
});
it('sets filter result', () => {
// arrange
const expectedFilter = new FilterResultStub();
// act
const sut = FilterChange.forApply(expectedFilter);
// assert
const actualFilter = sut.filter;
const actualFilter = (sut.action as ApplyFilterAction).filter;
expect(actualFilter).to.equal(expectedFilter);
});
it('sets action as expected', () => {
@@ -33,7 +22,7 @@ describe('FilterChange', () => {
// act
const sut = FilterChange.forApply(new FilterResultStub());
// assert
const actualAction = sut.actionType;
const actualAction = sut.action.type;
expect(actualAction).to.equal(expectedAction);
});
});
@@ -44,7 +33,7 @@ describe('FilterChange', () => {
// act
const sut = FilterChange.forClear();
// assert
const actualFilter = sut.filter;
const actualFilter = (sut.action as ApplyFilterAction).filter;
expect(actualFilter).to.equal(expectedFilter);
});
it('sets action as expected', () => {
@@ -53,23 +42,11 @@ describe('FilterChange', () => {
// act
const sut = FilterChange.forClear();
// assert
const actualAction = sut.actionType;
const actualAction = sut.action.type;
expect(actualAction).to.equal(expectedAction);
});
});
describe('visit', () => {
describe('throws when visitor is absent', () => {
itEachAbsentObjectValue((absentValue) => {
// arrange
const expectedError = 'missing visitor';
const visitorValue = absentValue;
const sut = FilterChange.forClear();
// act
const act = () => sut.visit(visitorValue);
// assert
expect(act).to.throw(expectedError);
});
});
describe('onClear', () => {
itVisitsOnce(
() => FilterChange.forClear(),
@@ -99,7 +76,7 @@ function itVisitsOnce(sutFactory: () => FilterChange) {
it('visits', () => {
// arrange
const sut = sutFactory();
const expectedType = sut.actionType;
const expectedType = sut.action.type;
const visitor = new FilterChangeDetailsVisitorStub();
// act
sut.visit(visitor);
@@ -109,7 +86,7 @@ function itVisitsOnce(sutFactory: () => FilterChange) {
it('visits once', () => {
// arrange
const sut = sutFactory();
const expectedType = sut.actionType;
const expectedType = sut.action.type;
const visitor = new FilterChangeDetailsVisitorStub();
// act
sut.visit(visitor);

View File

@@ -7,13 +7,14 @@ import { FilterChangeDetailsStub } from '@tests/unit/shared/Stubs/FilterChangeDe
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
import { IFilterChangeDetails } from '@/application/Context/State/Filter/Event/IFilterChangeDetails';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
describe('UserFilter', () => {
describe('clearFilter', () => {
it('signals when removing filter', () => {
// arrange
const expectedChange = FilterChangeDetailsStub.forClear();
let actualChange: IFilterChangeDetails;
let actualChange: IFilterChangeDetails | undefined;
const sut = new UserFilter(new CategoryCollectionStub());
sut.filterChanged.on((change) => {
actualChange = change;
@@ -21,6 +22,7 @@ describe('UserFilter', () => {
// act
sut.clearFilter();
// assert
expectExists(actualChange);
expect(actualChange).to.deep.equal(expectedChange);
});
it('sets currentFilter to undefined', () => {
@@ -160,6 +162,7 @@ describe('UserFilter', () => {
sut.applyFilter(filter);
// assert
const actual = sut.currentFilter;
expectExists(actual);
assert(actual);
});
});
@@ -171,13 +174,18 @@ describe('UserFilter', () => {
it(name, () => {
// arrange
const sut = new UserFilter(collection);
let actualFilterResult: IFilterResult;
let actualFilterResult: IFilterResult | undefined;
sut.filterChanged.on((filterResult) => {
actualFilterResult = filterResult.filter;
filterResult.visit({
onApply: (result) => {
actualFilterResult = result;
},
});
});
// act
sut.applyFilter(filter);
// assert
expectExists(actualFilterResult);
assert(actualFilterResult);
});
});

View File

@@ -80,7 +80,7 @@ function expectSameScripts(actual: readonly SelectedScript[], expected: readonly
`Scripts with different statuses:\n${
scriptsWithDifferentStatus
.map((s) => `[id: ${s.id}, actual status: ${s.revert}, `
+ `expected status: ${expected.find((existing) => existing.id === s.id).revert}]`)
+ `expected status: ${expected.find((existing) => existing.id === s.id)?.revert ?? 'unknown'}]`)
.join(' , ')
}`,
);