Refactor filter event handling to a unified event with visitor pattern
to simplify the code, avoid future bugs and provide better test
coverage.
This commit shifts from using separate `filtered` and `filterRemoved`
events to a singular, more expressive `filterChanged` event. The new
approach emits a detailed payload that explicitly indicates the filter
action and the associated filter data. The event object unifies the way
the presentation layer reacts to the events.
Benefits with this approach include:
- Simplifying event listeners by reducing the number of events to
handle.
- Increasing code clarity and reduces potential for oversight by
providing explicit action details in the event payload.
- Offering extensibility for future actions without introducing new
events.
- Providing visitor pattern to handle different kind of events in easy
and robust manner without code repetition.
Other changes:
- Refactor components handling of events to follow DRY and KISS
principles better.
- Refactor `UserFilter.spec.ts` to:
- Make it easier to add new tests.
- Increase code coverage by running all event-based tests on the
current property.
104 lines
3.9 KiB
TypeScript
104 lines
3.9 KiB
TypeScript
import 'mocha';
|
|
import { expect } from 'chai';
|
|
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
|
import { ApplicationCode } from '@/application/Context/State/Code/ApplicationCode';
|
|
import { CategoryCollectionState } from '@/application/Context/State/CategoryCollectionState';
|
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
|
import { IScript } from '@/domain/IScript';
|
|
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
|
import { CategoryStub } from '@tests/unit/shared/Stubs/CategoryStub';
|
|
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
|
|
|
|
describe('CategoryCollectionState', () => {
|
|
describe('code', () => {
|
|
it('initialized with empty code', () => {
|
|
// arrange
|
|
const collection = new CategoryCollectionStub();
|
|
const sut = new CategoryCollectionState(collection);
|
|
// act
|
|
const code = sut.code.current;
|
|
// assert
|
|
expect(!code);
|
|
});
|
|
it('reacts to selection changes as expected', () => {
|
|
// arrange
|
|
const collection = new CategoryCollectionStub()
|
|
.withAction(new CategoryStub(0).withScriptIds('scriptId'));
|
|
const selectionStub = new UserSelection(collection, []);
|
|
const expectedCodeGenerator = new ApplicationCode(selectionStub, collection.scripting);
|
|
selectionStub.selectAll();
|
|
const expectedCode = expectedCodeGenerator.current;
|
|
// act
|
|
const sut = new CategoryCollectionState(collection);
|
|
sut.selection.selectAll();
|
|
const actualCode = sut.code.current;
|
|
// assert
|
|
expect(actualCode).to.equal(expectedCode);
|
|
});
|
|
});
|
|
describe('os', () => {
|
|
it('same as its collection', () => {
|
|
// arrange
|
|
const expected = OperatingSystem.macOS;
|
|
const collection = new CategoryCollectionStub()
|
|
.withOs(expected);
|
|
// act
|
|
const sut = new CategoryCollectionState(collection);
|
|
// assert
|
|
const actual = sut.os;
|
|
expect(expected).to.equal(actual);
|
|
});
|
|
});
|
|
describe('selection', () => {
|
|
it('initialized with no selection', () => {
|
|
// arrange
|
|
const collection = new CategoryCollectionStub();
|
|
const sut = new CategoryCollectionState(collection);
|
|
// act
|
|
const actual = sut.selection.selectedScripts.length;
|
|
// assert
|
|
expect(actual).to.equal(0);
|
|
});
|
|
it('can select a script from current collection', () => {
|
|
// arrange
|
|
const expectedScript = new ScriptStub('scriptId');
|
|
const collection = new CategoryCollectionStub()
|
|
.withAction(new CategoryStub(0).withScript(expectedScript));
|
|
const sut = new CategoryCollectionState(collection);
|
|
// act
|
|
sut.selection.selectAll();
|
|
// assert
|
|
expect(sut.selection.selectedScripts.length).to.equal(1);
|
|
expect(sut.selection.isSelected(expectedScript.id)).to.equal(true);
|
|
});
|
|
});
|
|
describe('filter', () => {
|
|
it('initialized with an empty filter', () => {
|
|
// arrange
|
|
const collection = new CategoryCollectionStub();
|
|
const sut = new CategoryCollectionState(collection);
|
|
// act
|
|
const actual = sut.filter.currentFilter;
|
|
// assert
|
|
expect(actual).to.equal(undefined);
|
|
});
|
|
it('can match a script from current collection', () => {
|
|
// arrange
|
|
const scriptNameFilter = 'scriptName';
|
|
const expectedScript = new ScriptStub('scriptId')
|
|
.withName(scriptNameFilter);
|
|
const collection = new CategoryCollectionStub()
|
|
.withAction(new CategoryStub(0).withScript(expectedScript));
|
|
const sut = new CategoryCollectionState(collection);
|
|
// act
|
|
let actualScript: IScript | undefined;
|
|
sut.filter.filterChanged.on((result) => {
|
|
[actualScript] = result.filter?.scriptMatches ?? [undefined];
|
|
});
|
|
sut.filter.applyFilter(scriptNameFilter);
|
|
// assert
|
|
expect(expectedScript).to.equal(actualScript);
|
|
});
|
|
});
|
|
});
|