Files
privacy.sexy/tests/unit/presentation/components/Scripts/View/Tree/TreeViewAdapter/UseSelectedScriptNodeIds.spec.ts
undergroundwires e17744faf0 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.
2024-09-26 16:07:37 +02:00

99 lines
4.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { useSelectedScriptNodeIds } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/UseSelectedScriptNodeIds';
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
import { createNodeIdForExecutable } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
import type { Script } from '@/domain/Executables/Script/Script';
import { UseUserSelectionStateStub } from '@tests/unit/shared/Stubs/UseUserSelectionStateStub';
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', () => {
// arrange
const { useSelectionStateStub, returnObject } = runHook();
useSelectionStateStub.withSelectedScripts([]);
// act
const actualIds = returnObject.selectedScriptNodeIds.value;
// assert
expect(actualIds).to.have.lengthOf(0);
});
describe('returns correct node IDs for selected scripts', () => {
it('immediately', () => {
// arrange
const selectedScripts = [
new SelectedScriptStub(new ScriptStub('id-1')),
new SelectedScriptStub(new ScriptStub('id-2')),
];
const parsedNodeIds = new Map<Script, TreeNodeId>([
[selectedScripts[0].script, 'expected-id-1'],
[selectedScripts[1].script, 'expected-id-2'],
]);
const useSelectionStateStub = new UseUserSelectionStateStub()
.withSelectedScripts(selectedScripts);
const { returnObject } = runHook({
scriptNodeIdParser: createNodeIdParserFromMap(parsedNodeIds),
useSelectionState: useSelectionStateStub,
});
// act
const actualIds = returnObject.selectedScriptNodeIds.value;
// assert
const expectedNodeIds = [...parsedNodeIds.values()];
expect(actualIds).to.have.lengthOf(expectedNodeIds.length);
expect(actualIds).to.include.members(expectedNodeIds);
});
it('when the selection state changes', () => {
// arrange
const initialScripts = new Array<SelectedScript>();
const changedScripts = [
new SelectedScriptStub(new ScriptStub('id-1')),
new SelectedScriptStub(new ScriptStub('id-2')),
];
const parsedNodeIds = new Map<Script, TreeNodeId>([
[changedScripts[0].script, 'expected-id-1'],
[changedScripts[1].script, 'expected-id-2'],
]);
const useSelectionStateStub = new UseUserSelectionStateStub()
.withSelectedScripts(initialScripts);
const { returnObject } = runHook({
scriptNodeIdParser: createNodeIdParserFromMap(parsedNodeIds),
useSelectionState: useSelectionStateStub,
});
// act
useSelectionStateStub.withSelectedScripts(changedScripts);
const actualIds = returnObject.selectedScriptNodeIds.value;
// assert
const expectedNodeIds = [...parsedNodeIds.values()];
expect(actualIds).to.have.lengthOf(expectedNodeIds.length);
expect(actualIds).to.include.members(expectedNodeIds);
});
});
});
type NodeIdParser = typeof createNodeIdForExecutable;
function createNodeIdParserFromMap(scriptToIdMap: Map<Executable, TreeNodeId>): NodeIdParser {
return (script) => {
const expectedId = scriptToIdMap.get(script);
if (!expectedId) {
throw new Error(`No mapped ID for script: ${JSON.stringify(script)}`);
}
return expectedId;
};
}
function runHook(scenario?: {
readonly scriptNodeIdParser?: NodeIdParser,
readonly useSelectionState?: UseUserSelectionStateStub,
}) {
const useSelectionStateStub = scenario?.useSelectionState ?? new UseUserSelectionStateStub();
const nodeIdParser: NodeIdParser = scenario?.scriptNodeIdParser
?? ((script) => script.executableId);
const returnObject = useSelectedScriptNodeIds(useSelectionStateStub.get(), nodeIdParser);
return {
returnObject,
useSelectionStateStub,
};
}