Key highlights: - Written from scratch to cater specifically to privacy.sexy's needs and requirements. - The visual look mimics the previous component with minimal changes, but its internal code is completely rewritten. - Lays groundwork for future functionalities like the "expand all" button a flat view mode as discussed in #158. - Facilitates the transition to Vue 3 by omitting the Vue 2.0 dependent `liquour-tree` as part of #230. Improvements and features: - Caching for quicker node queries. - Gradual rendering of nodes that introduces a noticable boost in performance, particularly during search/filtering. - `TreeView` solely governs the check states of branch nodes. Changes: - Keyboard interactions now alter the background color to highlight the focused item. Previously, it was changing the color of the text. - Better state management with clear separation of concerns: - `TreeView` exclusively manages indeterminate states. - `TreeView` solely governs the check states of branch nodes. - Introduce transaction pattern to update state in batches to minimize amount of events handled. - Improve keyboard focus, style background instead of foreground. Use hover/touch color on keyboard focus. - `SelectableTree` has been removed. Instead, `TreeView` is now directly integrated with `ScriptsTree`. - `ScriptsTree` has been refactored to incorporate hooks for clearer code and separation of duties. - Adopt Vue-idiomatic bindings instead of keeping a reference of the tree component. - Simplify and change filter event management. - Abandon global styles in favor of class-scoped styles. - Use global mixins with descriptive names to clarify indended functionality.
76 lines
2.9 KiB
TypeScript
76 lines
2.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { shallowMount } from '@vue/test-utils';
|
|
import { useSelectedScriptNodeIds } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/UseSelectedScriptNodeIds';
|
|
import { InjectionKeys } from '@/presentation/injectionSymbols';
|
|
import { UseAutoUnsubscribedEventsStub } from '@tests/unit/shared/Stubs/UseAutoUnsubscribedEventsStub';
|
|
import { UseCollectionStateStub } from '@tests/unit/shared/Stubs/UseCollectionStateStub';
|
|
import { CategoryCollectionStateStub } from '@tests/unit/shared/Stubs/CategoryCollectionStateStub';
|
|
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
|
|
import { getScriptNodeId } from '@/presentation/components/Scripts/View/Tree/TreeViewAdapter/CategoryNodeMetadataConverter';
|
|
import { IScript } from '@/domain/IScript';
|
|
|
|
describe('useSelectedScriptNodeIds', () => {
|
|
it('returns empty array when no scripts are selected', () => {
|
|
// arrange
|
|
const { useStateStub, returnObject } = mountWrapperComponent();
|
|
useStateStub.withState(new CategoryCollectionStateStub().withSelectedScripts([]));
|
|
// act
|
|
const actualIds = returnObject.selectedScriptNodeIds.value;
|
|
// assert
|
|
expect(actualIds).to.have.lengthOf(0);
|
|
});
|
|
|
|
it('returns correct node IDs for selected scripts', () => {
|
|
// arrange
|
|
const selectedScripts = [
|
|
new SelectedScriptStub('id-1'),
|
|
new SelectedScriptStub('id-2'),
|
|
];
|
|
const parsedNodeIds = new Map<IScript, string>([
|
|
[selectedScripts[0].script, 'expected-id-1'],
|
|
[selectedScripts[1].script, 'expected-id-1'],
|
|
]);
|
|
const { useStateStub, returnObject } = mountWrapperComponent({
|
|
scriptNodeIdParser: (script) => parsedNodeIds.get(script),
|
|
});
|
|
useStateStub.triggerOnStateChange({
|
|
newState: new CategoryCollectionStateStub().withSelectedScripts(selectedScripts),
|
|
immediateOnly: true,
|
|
});
|
|
// act
|
|
const actualIds = returnObject.selectedScriptNodeIds.value;
|
|
// assert
|
|
const expectedNodeIds = [...parsedNodeIds.values()];
|
|
expect(actualIds).to.have.lengthOf(expectedNodeIds.length);
|
|
expect(actualIds).to.include.members(expectedNodeIds);
|
|
});
|
|
});
|
|
|
|
function mountWrapperComponent(scenario?: {
|
|
readonly scriptNodeIdParser?: typeof getScriptNodeId,
|
|
}) {
|
|
const useStateStub = new UseCollectionStateStub();
|
|
const nodeIdParser: typeof getScriptNodeId = scenario?.scriptNodeIdParser
|
|
?? ((script) => script.id);
|
|
let returnObject: ReturnType<typeof useSelectedScriptNodeIds>;
|
|
|
|
shallowMount({
|
|
setup() {
|
|
returnObject = useSelectedScriptNodeIds(nodeIdParser);
|
|
},
|
|
template: '<div></div>',
|
|
}, {
|
|
provide: {
|
|
[InjectionKeys.useCollectionState as symbol]:
|
|
() => useStateStub.get(),
|
|
[InjectionKeys.useAutoUnsubscribedEvents as symbol]:
|
|
() => new UseAutoUnsubscribedEventsStub().get(),
|
|
},
|
|
});
|
|
|
|
return {
|
|
returnObject,
|
|
useStateStub,
|
|
};
|
|
}
|