fix script revert activating recommendation level
Reverting any single of the scripts from standard recommendation pool shows "Standard" selection as selected which is wrong. This commit fixes it, refactors selection handling in a separate class and it also adds missing tests. It removes UserSelection.totalSelected propertty in favor of using UserSelection.selectedScripts.length to provide unified way of accessing the information.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { CategoryCollectionStateStub } from '@tests/unit/stubs/CategoryCollectionStateStub';
|
||||
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
|
||||
|
||||
export class SelectionStateTestScenario {
|
||||
public readonly all: readonly SelectedScript[];
|
||||
public readonly allStandard: readonly SelectedScript[];
|
||||
public readonly someStandard: readonly SelectedScript[];
|
||||
public readonly someStrict: readonly SelectedScript[];
|
||||
public readonly allStrict: readonly SelectedScript[];
|
||||
public readonly someUnrecommended: readonly SelectedScript[];
|
||||
public readonly allUnrecommended: readonly SelectedScript[];
|
||||
constructor() {
|
||||
this.someStandard = createSelectedScripts(RecommendationLevel.Standard, 'standard-some-1', 'standard-some-2');
|
||||
this.allStandard = [...this.someStandard, ...createSelectedScripts(RecommendationLevel.Standard, 'standard-all-1', 'standard-all-2')];
|
||||
this.someStrict = createSelectedScripts(RecommendationLevel.Strict, 'strict-some-1', 'strict-some-2');
|
||||
this.allStrict = [...this.someStrict, ...createSelectedScripts(RecommendationLevel.Strict, 'strict-all-1', 'strict-all-2')];
|
||||
this.someUnrecommended = createSelectedScripts(undefined, 'unrecommended-some-1', 'unrecommended-some-2');
|
||||
this.allUnrecommended = [...this.someUnrecommended, ...createSelectedScripts(undefined, 'unrecommended-all-1', 'unrecommended-all-2')];
|
||||
this.all = [...this.allStandard, ...this.allStrict, ...this.allUnrecommended];
|
||||
}
|
||||
public generateState(selectedScripts: readonly SelectedScript[]) {
|
||||
const allScripts = this.all.map((s) => s.script);
|
||||
return new CategoryCollectionStateStub(allScripts)
|
||||
.withSelectedScripts(selectedScripts);
|
||||
}
|
||||
}
|
||||
|
||||
function createSelectedScripts(level?: RecommendationLevel, ...ids: string[]) {
|
||||
return ids.map((id) => new SelectedScript(new ScriptStub(id).withLevel(level), false));
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { SelectionType, SelectionTypeHandler } from '@/presentation/components/Scripts/Menu/Selector/SelectionTypeHandler';
|
||||
import { scrambledEqual } from '@/application/Common/Array';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { SelectionStateTestScenario } from './SelectionStateTestScenario';
|
||||
|
||||
describe('SelectionTypeHandler', () => {
|
||||
describe('ctor', () => {
|
||||
it('throws when state is undefined', () => {
|
||||
// arrange
|
||||
const expectedError = 'undefined state';
|
||||
const state = undefined;
|
||||
// act
|
||||
const sut = () => new SelectionTypeHandler(state);
|
||||
// assert
|
||||
expect(sut).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
describe('selectType', () => {
|
||||
it('throws when type is custom', () => {
|
||||
// arrange
|
||||
const expectedError = 'cannot select custom type';
|
||||
const scenario = new SelectionStateTestScenario();
|
||||
const state = scenario.generateState([]);
|
||||
const sut = new SelectionTypeHandler(state);
|
||||
// act
|
||||
const act = () => sut.selectType(SelectionType.Custom);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
describe('select types as expected', () => {
|
||||
// arrange
|
||||
const scenario = new SelectionStateTestScenario();
|
||||
const initialScriptsCases = [{
|
||||
name: 'when nothing is selected',
|
||||
initialScripts: [],
|
||||
}, {
|
||||
name: 'when some scripts are selected',
|
||||
initialScripts: [...scenario.allStandard, ...scenario.someStrict],
|
||||
}, {
|
||||
name: 'when all scripts are selected',
|
||||
initialScripts: scenario.all,
|
||||
} ];
|
||||
for (const initialScriptsCase of initialScriptsCases) {
|
||||
describe(initialScriptsCase.name, () => {
|
||||
const state = scenario.generateState(initialScriptsCase.initialScripts);
|
||||
const sut = new SelectionTypeHandler(state);
|
||||
const typeExpectations = [{
|
||||
input: SelectionType.None,
|
||||
output: [],
|
||||
}, {
|
||||
input: SelectionType.Standard,
|
||||
output: scenario.allStandard,
|
||||
}, {
|
||||
input: SelectionType.Strict,
|
||||
output: [...scenario.allStandard, ...scenario.allStrict],
|
||||
}, {
|
||||
input: SelectionType.All,
|
||||
output: scenario.all,
|
||||
}];
|
||||
for (const expectation of typeExpectations) {
|
||||
// act
|
||||
it(`${SelectionType[expectation.input]} returns as expected`, () => {
|
||||
sut.selectType(expectation.input);
|
||||
// assert
|
||||
const actual = state.selection.selectedScripts;
|
||||
const expected = expectation.output;
|
||||
expect(scrambledEqual(actual, expected));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('getCurrentSelectionType', () => {
|
||||
// arrange
|
||||
const scenario = new SelectionStateTestScenario();
|
||||
const testCases = [{
|
||||
name: 'when nothing is selected',
|
||||
selection: [],
|
||||
expected: SelectionType.None,
|
||||
}, {
|
||||
name: 'when some standard scripts are selected',
|
||||
selection: scenario.someStandard,
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all standard scripts are selected',
|
||||
selection: scenario.allStandard,
|
||||
expected: SelectionType.Standard,
|
||||
}, {
|
||||
name: 'when all standard and some strict scripts are selected',
|
||||
selection: [...scenario.allStandard, ...scenario.someStrict],
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all standard and strict scripts are selected',
|
||||
selection: [...scenario.allStandard, ...scenario.allStrict],
|
||||
expected: SelectionType.Strict,
|
||||
}, {
|
||||
name: 'when strict scripts are selected but not standard',
|
||||
selection: scenario.allStrict,
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all standard and strict, and some unrecommended are selected',
|
||||
selection: [...scenario.allStandard, ...scenario.allStrict, ...scenario.someUnrecommended],
|
||||
expected: SelectionType.Custom,
|
||||
}, {
|
||||
name: 'when all scripts are selected',
|
||||
selection: scenario.all,
|
||||
expected: SelectionType.All,
|
||||
} ];
|
||||
for (const testCase of testCases) {
|
||||
it(testCase.name, () => {
|
||||
const state = scenario.generateState(testCase.selection);
|
||||
const sut = new SelectionTypeHandler(state);
|
||||
// act
|
||||
const actual = sut.getCurrentSelectionType();
|
||||
// assert
|
||||
expect(actual).to.deep.equal(testCase.expected,
|
||||
`Actual: "${SelectionType[actual]}", expected: "${SelectionType[testCase.expected]}"` +
|
||||
`\nSelection: ${printSelection()}`);
|
||||
function printSelection() {
|
||||
return `total: ${testCase.selection.length}\n` +
|
||||
'scripts:\n' +
|
||||
testCase.selection
|
||||
.map((s) => `{ id: ${s.script.id}, level: ${s.script.level === undefined ? 'unknown' : RecommendationLevel[s.script.level]} }`)
|
||||
.join(' | ');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user