Refactor executable IDs to use strings #262
This commit unifies executable ID structure across categories and scripts, paving the way for more complex ID solutions for #262. It also refactors related code to adapt to the changes. Key changes: - Change numeric IDs to string IDs for categories - Use named types for string IDs to improve code clarity - Add unit tests to verify ID uniqueness Other supporting changes: - Separate concerns in entities for data access and executables by using separate abstractions (`Identifiable` and `RepositoryEntity`) - Simplify usage and construction of entities. - Remove `BaseEntity` for simplicity. - Move creation of categories/scripts to domain layer - Refactor CategoryCollection for better validation logic isolation - Rename some categories to keep the names (used as pseudo-IDs) unique on Windows.
This commit is contained in:
@@ -3,12 +3,12 @@ import { CategoryReverter } from '@/presentation/components/Scripts/View/Tree/No
|
||||
import { CategoryStub } from '@tests/unit/shared/Stubs/CategoryStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
|
||||
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
||||
import { getCategoryNodeId } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
import { UserSelectionStub } from '@tests/unit/shared/Stubs/UserSelectionStub';
|
||||
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import { CategorySelectionStub } from '@tests/unit/shared/Stubs/CategorySelectionStub';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import { createExecutableIdFromNodeId } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
|
||||
describe('CategoryReverter', () => {
|
||||
describe('getState', () => {
|
||||
@@ -122,8 +122,8 @@ describe('CategoryReverter', () => {
|
||||
}) => {
|
||||
it(description, () => {
|
||||
// arrange
|
||||
const category = new CategoryStub(1).withScripts(...allScripts);
|
||||
const categoryNodeId = getCategoryNodeId(category);
|
||||
const category = new CategoryStub('parent-category-id').withScripts(...allScripts);
|
||||
const categoryNodeId = createExecutableIdFromNodeId(category.executableId);
|
||||
const collection = new CategoryCollectionStub().withAction(category);
|
||||
const categoryReverter = new CategoryReverter(categoryNodeId, collection);
|
||||
const selectedScripts = selectScripts(allScripts);
|
||||
@@ -157,8 +157,8 @@ describe('CategoryReverter', () => {
|
||||
new ScriptStub('reversible').withReversibility(true),
|
||||
new ScriptStub('reversible2').withReversibility(true),
|
||||
];
|
||||
const category = new CategoryStub(1).withScripts(...allScripts);
|
||||
const nodeId = getCategoryNodeId(category);
|
||||
const category = new CategoryStub('parent-category').withScripts(...allScripts);
|
||||
const nodeId = createExecutableIdFromNodeId(category.executableId);
|
||||
const collection = new CategoryCollectionStub().withAction(category);
|
||||
const categorySelection = new CategorySelectionStub();
|
||||
const categoryReverter = new CategoryReverter(nodeId, collection);
|
||||
@@ -170,7 +170,7 @@ describe('CategoryReverter', () => {
|
||||
);
|
||||
// assert
|
||||
const actualRevertState = categorySelection.isCategorySelected(
|
||||
category.id,
|
||||
category.executableId,
|
||||
expectedRevertState,
|
||||
);
|
||||
expect(actualRevertState).to.equal(true);
|
||||
|
||||
@@ -5,15 +5,16 @@ import { CategoryReverter } from '@/presentation/components/Scripts/View/Tree/No
|
||||
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '@tests/unit/shared/Stubs/CategoryStub';
|
||||
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
||||
import { getCategoryNodeId, getScriptNodeId } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
import { createNodeIdForExecutable } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
import { type NodeMetadata, NodeType } from '@/presentation/components/Scripts/View/Tree/NodeContent/NodeMetadata';
|
||||
import type { TreeNodeId } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
|
||||
|
||||
describe('ReverterFactory', () => {
|
||||
describe('getReverter', () => {
|
||||
it('gets CategoryReverter for category node', () => {
|
||||
it(`gets ${CategoryReverter.name} for category node`, () => {
|
||||
// arrange
|
||||
const category = new CategoryStub(0).withScriptIds('55');
|
||||
const node = getNodeContentStub(getCategoryNodeId(category), NodeType.Category);
|
||||
const category = new CategoryStub('test-action-category').withScriptIds('55');
|
||||
const node = getNodeContentStub(createNodeIdForExecutable(category), NodeType.Category);
|
||||
const collection = new CategoryCollectionStub()
|
||||
.withAction(category);
|
||||
// act
|
||||
@@ -21,21 +22,21 @@ describe('ReverterFactory', () => {
|
||||
// assert
|
||||
expect(result instanceof CategoryReverter).to.equal(true);
|
||||
});
|
||||
it('gets ScriptReverter for script node', () => {
|
||||
it(`gets ${ScriptReverter.name} for script node`, () => {
|
||||
// arrange
|
||||
const script = new ScriptStub('test');
|
||||
const node = getNodeContentStub(getScriptNodeId(script), NodeType.Script);
|
||||
const node = getNodeContentStub(createNodeIdForExecutable(script), NodeType.Script);
|
||||
const collection = new CategoryCollectionStub()
|
||||
.withAction(new CategoryStub(0).withScript(script));
|
||||
.withAction(new CategoryStub('test-action-category').withScript(script));
|
||||
// act
|
||||
const result = getReverter(node, collection);
|
||||
// assert
|
||||
expect(result instanceof ScriptReverter).to.equal(true);
|
||||
});
|
||||
});
|
||||
function getNodeContentStub(nodeId: string, type: NodeType): NodeMetadata {
|
||||
function getNodeContentStub(nodeId: TreeNodeId, type: NodeType): NodeMetadata {
|
||||
return {
|
||||
id: nodeId,
|
||||
executableId: nodeId,
|
||||
text: 'text',
|
||||
isReversible: false,
|
||||
docs: [],
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { ScriptReverter } from '@/presentation/components/Scripts/View/Tree/NodeContent/Reverter/ScriptReverter';
|
||||
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
||||
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
|
||||
import { getScriptNodeId } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
import { createNodeIdForExecutable } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
import { UserSelectionStub } from '@tests/unit/shared/Stubs/UserSelectionStub';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import { ScriptSelectionStub } from '@tests/unit/shared/Stubs/ScriptSelectionStub';
|
||||
@@ -11,7 +11,7 @@ describe('ScriptReverter', () => {
|
||||
describe('getState', () => {
|
||||
// arrange
|
||||
const script = new ScriptStub('id');
|
||||
const nodeId = getScriptNodeId(script);
|
||||
const nodeId = createNodeIdForExecutable(script);
|
||||
const testScenarios: ReadonlyArray<{
|
||||
readonly description: string;
|
||||
readonly selectedScripts: readonly SelectedScript[];
|
||||
@@ -98,7 +98,7 @@ describe('ScriptReverter', () => {
|
||||
expectedRevert: false,
|
||||
},
|
||||
];
|
||||
const nodeId = getScriptNodeId(script);
|
||||
const nodeId = createNodeIdForExecutable(script);
|
||||
testScenarios.forEach((
|
||||
{ description, selection, expectedRevert },
|
||||
) => {
|
||||
@@ -111,7 +111,9 @@ describe('ScriptReverter', () => {
|
||||
// act
|
||||
sut.selectWithRevertState(revertState, userSelection);
|
||||
// assert
|
||||
expect(scriptSelection.isScriptSelected(script.id, expectedRevert)).to.equal(true);
|
||||
const isActuallySelected = scriptSelection
|
||||
.isScriptSelected(script.executableId, expectedRevert);
|
||||
expect(isActuallySelected).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,13 +3,14 @@ import { TreeNodeManager } from '@/presentation/components/Scripts/View/Tree/Tre
|
||||
import { itEachAbsentObjectValue, itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import { TreeNodeHierarchy } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/Hierarchy/TreeNodeHierarchy';
|
||||
import { TreeNodeState } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/State/TreeNodeState';
|
||||
import type { TreeNodeId } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
|
||||
|
||||
describe('TreeNodeManager', () => {
|
||||
describe('constructor', () => {
|
||||
describe('id', () => {
|
||||
it('should initialize with the provided id', () => {
|
||||
// arrange
|
||||
const expectedId = 'test-id';
|
||||
const expectedId: TreeNodeId = 'test-id';
|
||||
// act
|
||||
const node = new TreeNodeManager(expectedId);
|
||||
// assert
|
||||
@@ -18,9 +19,10 @@ describe('TreeNodeManager', () => {
|
||||
describe('should throw an error if id is not provided', () => {
|
||||
itEachAbsentStringValue((absentId) => {
|
||||
// arrange
|
||||
const id = absentId as TreeNodeId;
|
||||
const expectedError = 'missing id';
|
||||
// act
|
||||
const act = () => new TreeNodeManager(absentId);
|
||||
const act = () => new TreeNodeManager(id);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
}, { excludeNull: true, excludeUndefined: true });
|
||||
|
||||
@@ -5,31 +5,36 @@ 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 {
|
||||
getCategoryId, getCategoryNodeId, getScriptId,
|
||||
getScriptNodeId, parseAllCategories, parseSingleCategory,
|
||||
createExecutableIdFromNodeId,
|
||||
createNodeIdForExecutable,
|
||||
parseAllCategories,
|
||||
parseSingleCategory,
|
||||
} from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
import { ExecutableType } from '@/application/Parser/Executable/Validation/ExecutableType';
|
||||
import type { NodeMetadata } from '@/presentation/components/Scripts/View/Tree/NodeContent/NodeMetadata';
|
||||
import { expectExists } from '@tests/shared/Assertions/ExpectExists';
|
||||
import type { ExecutableId } from '@/domain/Executables/Identifiable';
|
||||
|
||||
describe('CategoryNodeMetadataConverter', () => {
|
||||
it('can convert script id and back', () => {
|
||||
// arrange
|
||||
const script = new ScriptStub('test');
|
||||
const expectedScriptId: ExecutableId = 'expected-script-id';
|
||||
const script = new ScriptStub(expectedScriptId);
|
||||
// act
|
||||
const nodeId = getScriptNodeId(script);
|
||||
const scriptId = getScriptId(nodeId);
|
||||
const nodeId = createNodeIdForExecutable(script);
|
||||
const actualScriptId = createExecutableIdFromNodeId(nodeId);
|
||||
// assert
|
||||
expect(scriptId).to.equal(script.id);
|
||||
expect(actualScriptId).to.equal(expectedScriptId);
|
||||
});
|
||||
it('can convert category id and back', () => {
|
||||
// arrange
|
||||
const category = new CategoryStub(55);
|
||||
const expectedCategoryId: ExecutableId = 'expected-category-id';
|
||||
const category = new CategoryStub(expectedCategoryId);
|
||||
// act
|
||||
const nodeId = getCategoryNodeId(category);
|
||||
const scriptId = getCategoryId(nodeId);
|
||||
const nodeId = createNodeIdForExecutable(category);
|
||||
const actualCategoryId = createExecutableIdFromNodeId(nodeId);
|
||||
// assert
|
||||
expect(scriptId).to.equal(category.id);
|
||||
expect(actualCategoryId).to.equal(expectedCategoryId);
|
||||
});
|
||||
describe('parseSingleCategory', () => {
|
||||
it('throws error if parent category cannot be retrieved', () => {
|
||||
@@ -38,32 +43,45 @@ describe('CategoryNodeMetadataConverter', () => {
|
||||
const collection = new CategoryCollectionStub();
|
||||
collection.getCategory = () => { throw new Error(expectedError); };
|
||||
// act
|
||||
const act = () => parseSingleCategory(31, collection);
|
||||
const act = () => parseSingleCategory('unimportant-id', collection);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
it('can parse when category has sub categories', () => {
|
||||
// arrange
|
||||
const categoryId = 31;
|
||||
const firstSubCategory = new CategoryStub(11).withScriptIds('111', '112');
|
||||
const secondSubCategory = new CategoryStub(categoryId)
|
||||
.withCategory(new CategoryStub(33).withScriptIds('331', '331'))
|
||||
.withCategory(new CategoryStub(44).withScriptIds('44'));
|
||||
const collection = new CategoryCollectionStub().withAction(new CategoryStub(categoryId)
|
||||
.withCategory(firstSubCategory)
|
||||
.withCategory(secondSubCategory));
|
||||
const parentCategoryId: ExecutableId = 'parent-category';
|
||||
const firstSubcategory = new CategoryStub('subcategory-1')
|
||||
.withScriptIds('subcategory-1-script-1', 'subcategory-1-script-2');
|
||||
const secondSubCategory = new CategoryStub('subcategory-2')
|
||||
.withCategory(
|
||||
new CategoryStub('subcategory-2-subcategory-1')
|
||||
.withScriptIds('subcategory-2-subcategory-1-script-1', 'subcategory-2-subcategory-1-script-2'),
|
||||
)
|
||||
.withCategory(
|
||||
new CategoryStub('subcategory-2-subcategory-2')
|
||||
.withScriptIds('subcategory-2-subcategory-2-script-1'),
|
||||
);
|
||||
const collection = new CategoryCollectionStub().withAction(
|
||||
new CategoryStub(parentCategoryId)
|
||||
.withCategory(firstSubcategory)
|
||||
.withCategory(secondSubCategory),
|
||||
);
|
||||
// act
|
||||
const nodes = parseSingleCategory(categoryId, collection);
|
||||
const nodes = parseSingleCategory(parentCategoryId, collection);
|
||||
// assert
|
||||
expectExists(nodes);
|
||||
expect(nodes).to.have.lengthOf(2);
|
||||
expectSameCategory(nodes[0], firstSubCategory);
|
||||
expectSameCategory(nodes[0], firstSubcategory);
|
||||
expectSameCategory(nodes[1], secondSubCategory);
|
||||
});
|
||||
it('can parse when category has sub scripts', () => {
|
||||
// arrange
|
||||
const categoryId = 31;
|
||||
const scripts = [new ScriptStub('script1'), new ScriptStub('script2'), new ScriptStub('script3')];
|
||||
const categoryId: ExecutableId = 'expected-category-id';
|
||||
const scripts: readonly Script[] = [
|
||||
new ScriptStub('script1'),
|
||||
new ScriptStub('script2'),
|
||||
new ScriptStub('script3'),
|
||||
];
|
||||
const collection = new CategoryCollectionStub()
|
||||
.withAction(new CategoryStub(categoryId).withScripts(...scripts));
|
||||
// act
|
||||
@@ -79,10 +97,11 @@ describe('CategoryNodeMetadataConverter', () => {
|
||||
it('parseAllCategories parses as expected', () => {
|
||||
// arrange
|
||||
const collection = new CategoryCollectionStub()
|
||||
.withAction(new CategoryStub(0).withScriptIds('1, 2'))
|
||||
.withAction(new CategoryStub(1).withCategories(
|
||||
new CategoryStub(3).withScriptIds('3', '4'),
|
||||
new CategoryStub(4).withCategory(new CategoryStub(5).withScriptIds('6')),
|
||||
.withAction(new CategoryStub('category-1').withScriptIds('1, 2'))
|
||||
.withAction(new CategoryStub('category-2').withCategories(
|
||||
new CategoryStub('category-2-subcategory-1').withScriptIds('3', '4'),
|
||||
new CategoryStub('category-2-subcategory-1')
|
||||
.withCategory(new CategoryStub('category-2-subcategory-1-subcategory-1').withScriptIds('6')),
|
||||
));
|
||||
// act
|
||||
const nodes = parseAllCategories(collection);
|
||||
@@ -100,8 +119,8 @@ function isReversible(category: Category): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (category.subCategories) {
|
||||
if (category.subCategories.some((c) => !isReversible(c))) {
|
||||
if (category.subcategories) {
|
||||
if (category.subcategories.some((c) => !isReversible(c))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -110,17 +129,17 @@ function isReversible(category: Category): boolean {
|
||||
|
||||
function expectSameCategory(node: NodeMetadata, category: Category): void {
|
||||
expect(node.type).to.equal(ExecutableType.Category, getErrorMessage('type'));
|
||||
expect(node.id).to.equal(getCategoryNodeId(category), getErrorMessage('id'));
|
||||
expect(node.executableId).to.equal(createNodeIdForExecutable(category), getErrorMessage('id'));
|
||||
expect(node.docs).to.equal(category.docs, getErrorMessage('docs'));
|
||||
expect(node.text).to.equal(category.name, getErrorMessage('name'));
|
||||
expect(node.isReversible).to.equal(isReversible(category), getErrorMessage('isReversible'));
|
||||
expect(node.children).to.have.lengthOf(
|
||||
category.scripts.length + category.subCategories.length,
|
||||
category.scripts.length + category.subcategories.length,
|
||||
getErrorMessage('total children'),
|
||||
);
|
||||
if (category.subCategories) {
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
expectSameCategory(node.children[i], category.subCategories[i]);
|
||||
if (category.subcategories) {
|
||||
for (let i = 0; i < category.subcategories.length; i++) {
|
||||
expectSameCategory(node.children[i], category.subcategories[i]);
|
||||
}
|
||||
}
|
||||
if (category.scripts) {
|
||||
@@ -137,7 +156,7 @@ function expectSameCategory(node: NodeMetadata, category: Category): void {
|
||||
|
||||
function expectSameScript(node: NodeMetadata, script: Script): void {
|
||||
expect(node.type).to.equal(ExecutableType.Script, getErrorMessage('type'));
|
||||
expect(node.id).to.equal(getScriptNodeId(script), getErrorMessage('id'));
|
||||
expect(node.executableId).to.equal(createNodeIdForExecutable(script), getErrorMessage('id'));
|
||||
expect(node.docs).to.equal(script.docs, getErrorMessage('docs'));
|
||||
expect(node.text).to.equal(script.name, getErrorMessage('name'));
|
||||
expect(node.isReversible).to.equal(script.canRevert(), getErrorMessage('canRevert'));
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
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 { getScriptNodeId } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
||||
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';
|
||||
|
||||
describe('useSelectedScriptNodeIds', () => {
|
||||
it('returns an empty array when no scripts are selected', () => {
|
||||
@@ -23,7 +25,7 @@ describe('useSelectedScriptNodeIds', () => {
|
||||
new SelectedScriptStub(new ScriptStub('id-1')),
|
||||
new SelectedScriptStub(new ScriptStub('id-2')),
|
||||
];
|
||||
const parsedNodeIds = new Map<Script, string>([
|
||||
const parsedNodeIds = new Map<Script, TreeNodeId>([
|
||||
[selectedScripts[0].script, 'expected-id-1'],
|
||||
[selectedScripts[1].script, 'expected-id-2'],
|
||||
]);
|
||||
@@ -47,7 +49,7 @@ describe('useSelectedScriptNodeIds', () => {
|
||||
new SelectedScriptStub(new ScriptStub('id-1')),
|
||||
new SelectedScriptStub(new ScriptStub('id-2')),
|
||||
];
|
||||
const parsedNodeIds = new Map<Script, string>([
|
||||
const parsedNodeIds = new Map<Script, TreeNodeId>([
|
||||
[changedScripts[0].script, 'expected-id-1'],
|
||||
[changedScripts[1].script, 'expected-id-2'],
|
||||
]);
|
||||
@@ -68,9 +70,9 @@ describe('useSelectedScriptNodeIds', () => {
|
||||
});
|
||||
});
|
||||
|
||||
type ScriptNodeIdParser = typeof getScriptNodeId;
|
||||
type NodeIdParser = typeof createNodeIdForExecutable;
|
||||
|
||||
function createNodeIdParserFromMap(scriptToIdMap: Map<Script, string>): ScriptNodeIdParser {
|
||||
function createNodeIdParserFromMap(scriptToIdMap: Map<Executable, TreeNodeId>): NodeIdParser {
|
||||
return (script) => {
|
||||
const expectedId = scriptToIdMap.get(script);
|
||||
if (!expectedId) {
|
||||
@@ -81,12 +83,12 @@ function createNodeIdParserFromMap(scriptToIdMap: Map<Script, string>): ScriptNo
|
||||
}
|
||||
|
||||
function runHook(scenario?: {
|
||||
readonly scriptNodeIdParser?: ScriptNodeIdParser,
|
||||
readonly scriptNodeIdParser?: NodeIdParser,
|
||||
readonly useSelectionState?: UseUserSelectionStateStub,
|
||||
}) {
|
||||
const useSelectionStateStub = scenario?.useSelectionState ?? new UseUserSelectionStateStub();
|
||||
const nodeIdParser: ScriptNodeIdParser = scenario?.scriptNodeIdParser
|
||||
?? ((script) => script.id);
|
||||
const nodeIdParser: NodeIdParser = scenario?.scriptNodeIdParser
|
||||
?? ((script) => script.executableId);
|
||||
const returnObject = useSelectedScriptNodeIds(useSelectionStateStub.get(), nodeIdParser);
|
||||
return {
|
||||
returnObject,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { TreeNodeStub } from '@tests/unit/shared/Stubs/TreeNodeStub';
|
||||
import { HierarchyAccessStub } from '@tests/unit/shared/Stubs/HierarchyAccessStub';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import type { TreeNode } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
|
||||
import type { TreeNode, TreeNodeId } from '@/presentation/components/Scripts/View/Tree/TreeView/Node/TreeNode';
|
||||
import { FilterChangeDetailsStub } from '@tests/unit/shared/Stubs/FilterChangeDetailsStub';
|
||||
import type { FilterChangeDetails } from '@/application/Context/State/Filter/Event/FilterChangeDetails';
|
||||
import { CategoryCollectionStateStub } from '@tests/unit/shared/Stubs/CategoryCollectionStateStub';
|
||||
@@ -216,29 +216,29 @@ function itExpectedFilterTriggeredEvent(
|
||||
{
|
||||
description: 'returns true when category exists',
|
||||
scriptMatches: [],
|
||||
categoryMatches: [new CategoryStub(1)],
|
||||
givenNode: createNode({ id: '1', hasParent: false }),
|
||||
categoryMatches: [new CategoryStub('category-match-1')],
|
||||
givenNode: createNode({ nodeId: 'category-match-1', hasParent: false }),
|
||||
expectedPredicateResult: true,
|
||||
},
|
||||
{
|
||||
description: 'returns true when script exists',
|
||||
scriptMatches: [new ScriptStub('a')],
|
||||
scriptMatches: [new ScriptStub('script-match-1')],
|
||||
categoryMatches: [],
|
||||
givenNode: createNode({ id: 'a', hasParent: true }),
|
||||
givenNode: createNode({ nodeId: 'script-match-1', hasParent: true }),
|
||||
expectedPredicateResult: true,
|
||||
},
|
||||
{
|
||||
description: 'returns false when category is missing',
|
||||
scriptMatches: [new ScriptStub('b')],
|
||||
categoryMatches: [new CategoryStub(2)],
|
||||
givenNode: createNode({ id: '1', hasParent: false }),
|
||||
scriptMatches: [new ScriptStub('script-match-1')],
|
||||
categoryMatches: [new CategoryStub('category-match-1')],
|
||||
givenNode: createNode({ nodeId: 'unrelated-node', hasParent: false }),
|
||||
expectedPredicateResult: false,
|
||||
},
|
||||
{
|
||||
description: 'finds false when script is missing',
|
||||
scriptMatches: [new ScriptStub('b')],
|
||||
categoryMatches: [new CategoryStub(1)],
|
||||
givenNode: createNode({ id: 'a', hasParent: true }),
|
||||
scriptMatches: [new ScriptStub('script-match-1')],
|
||||
categoryMatches: [new CategoryStub('category-match-1')],
|
||||
givenNode: createNode({ nodeId: 'unrelated-node', hasParent: true }),
|
||||
expectedPredicateResult: false,
|
||||
},
|
||||
];
|
||||
@@ -261,8 +261,8 @@ function itExpectedFilterTriggeredEvent(
|
||||
expect(event.value.predicate).toBeDefined();
|
||||
const actualPredicateResult = event.value.predicate(givenNode);
|
||||
expect(actualPredicateResult).to.equal(expectedPredicateResult, formatAssertionMessage([
|
||||
`Script matches (${scriptMatches.length}): [${scriptMatches.map((s) => s.id).join(', ')}]`,
|
||||
`Category matches (${categoryMatches.length}): [${categoryMatches.map((s) => s.id).join(', ')}]`,
|
||||
`Script matches (${scriptMatches.length}): [${scriptMatches.map((s) => s.executableId).join(', ')}]`,
|
||||
`Category matches (${categoryMatches.length}): [${categoryMatches.map((s) => s.executableId).join(', ')}]`,
|
||||
`Expected node: "${givenNode.id}"`,
|
||||
]));
|
||||
});
|
||||
@@ -270,12 +270,12 @@ function itExpectedFilterTriggeredEvent(
|
||||
}
|
||||
|
||||
function createNode(options: {
|
||||
readonly id: string;
|
||||
readonly nodeId: TreeNodeId;
|
||||
readonly hasParent: boolean;
|
||||
}): TreeNode {
|
||||
return new TreeNodeStub()
|
||||
.withId(options.id)
|
||||
.withMetadata(new NodeMetadataStub().withId(options.id))
|
||||
.withId(options.nodeId)
|
||||
.withMetadata(new NodeMetadataStub().withId(options.nodeId))
|
||||
.withHierarchy(options.hasParent
|
||||
? new HierarchyAccessStub().withParent(new TreeNodeStub())
|
||||
: new HierarchyAccessStub());
|
||||
|
||||
@@ -7,21 +7,22 @@ import { CategoryCollectionStateStub } from '@tests/unit/shared/Stubs/CategoryCo
|
||||
import { UseCollectionStateStub } from '@tests/unit/shared/Stubs/UseCollectionStateStub';
|
||||
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
|
||||
import { CategoryStub } from '@tests/unit/shared/Stubs/CategoryStub';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
|
||||
import type { NodeMetadata } from '@/presentation/components/Scripts/View/Tree/NodeContent/NodeMetadata';
|
||||
import { NodeMetadataStub } from '@tests/unit/shared/Stubs/NodeMetadataStub';
|
||||
import { convertToNodeInput } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/TreeNodeMetadataConverter';
|
||||
import { TreeInputNodeDataStub as TreeInputNodeData, TreeInputNodeDataStub } from '@tests/unit/shared/Stubs/TreeInputNodeDataStub';
|
||||
import type { ExecutableId } from '@/domain/Executables/Identifiable';
|
||||
|
||||
describe('useTreeViewNodeInput', () => {
|
||||
describe('when given categoryId', () => {
|
||||
it('sets input nodes correctly', async () => {
|
||||
// arrange
|
||||
const testCategoryIdRef = ref<number | undefined>();
|
||||
const testCategoryIdRef = ref<ExecutableId | undefined>();
|
||||
const {
|
||||
useStateStub, returnObject, parserMock, converterMock,
|
||||
} = mountWrapperComponent(testCategoryIdRef);
|
||||
const expectedCategoryId = 123;
|
||||
const expectedCategoryId: ExecutableId = 'expected-category-id';
|
||||
const expectedCategoryCollection = new CategoryCollectionStub().withAction(
|
||||
new CategoryStub(expectedCategoryId),
|
||||
);
|
||||
@@ -55,12 +56,12 @@ describe('useTreeViewNodeInput', () => {
|
||||
describe('when not given a categoryId', () => {
|
||||
it('sets input nodes correctly', () => {
|
||||
// arrange
|
||||
const testCategoryId = ref<number | undefined>();
|
||||
const testCategoryId = ref<ExecutableId | undefined>();
|
||||
const {
|
||||
useStateStub, returnObject, parserMock, converterMock,
|
||||
} = mountWrapperComponent(testCategoryId);
|
||||
const expectedCategoryCollection = new CategoryCollectionStub().withAction(
|
||||
new CategoryStub(123),
|
||||
new CategoryStub('expected-action-category'),
|
||||
);
|
||||
const expectedMetadata = [new NodeMetadataStub(), new NodeMetadataStub()];
|
||||
parserMock.setupParseAllScenario({
|
||||
@@ -88,7 +89,7 @@ describe('useTreeViewNodeInput', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function mountWrapperComponent(categoryIdRef: Ref<number | undefined>) {
|
||||
function mountWrapperComponent(categoryIdRef: Ref<ExecutableId | undefined>) {
|
||||
const useStateStub = new UseCollectionStateStub();
|
||||
const parserMock = mockCategoryNodeParser();
|
||||
const converterMock = mockConverter();
|
||||
@@ -146,7 +147,7 @@ function mockConverter() {
|
||||
}
|
||||
|
||||
interface ParseSingleScenario {
|
||||
readonly givenId: number;
|
||||
readonly givenId: ExecutableId;
|
||||
readonly givenCollection: ICategoryCollection;
|
||||
readonly parseResult: NodeMetadata[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user