Our algorithm was: reorderTabs.
Go to the subject itself for more details
CodeSandbox with a possible set of properties you may have come with: https://codesandbox.io/s/advent-of-pbt-day-14-solution-ng2b?file=/src/index.spec.ts&previewwindow=tests
For this algorithm I chose to generate the entries requested by the algorithm and not try to build them from intermediate inputs. In other words, I will re-use the same arbitrary in all the properties and check whether or not the algorithm does what it says as checking it did it is simpler than doing it.
The arbitrary we will re-use over and over for this algorithm is:
function tabsWithSelectionArb() {
return fc
.set(fc.nat(), { minLength: 2 })
.chain((tabs) =>
fc.record({
tabs: fc.constant(tabs),
selectedTabs: fc.subarray(tabs, {
minLength: 1,
maxLength: tabs.length - 1
})
})
)
.chain(({ tabs, selectedTabs }) =>
fc.record({
tabs: fc.constant(tabs),
selectedTabs: fc.constant(selectedTabs),
movePosition: fc.constantFrom(
...tabs.filter((t) => !selectedTabs.includes(t))
)
})
);
}
It just computes three valid and mutually compatible values for tabs
, selectedTabs
and movePosition
.
Now we have it, let's see which properties we could create thanks to it.
Property 1: should group selected tabs together
for any constraints
it should move the selected tabs close to each others
in other words we should not have any other tabs between them after the move
Written with fast-check:
it("should group selected tabs together", () => {
fc.assert(
fc.property(
tabsWithSelectionArb(),
({ tabs, selectedTabs, movePosition }) => {
// Arrange / Act
const newTabs = reorderTabs(tabs, selectedTabs, movePosition);
// Assert
const startMovedSelection = newTabs.indexOf(selectedTabs[0]);
expect(
newTabs.slice(
startMovedSelection,
startMovedSelection + selectedTabs.length
)
).toEqual(selectedTabs);
}
)
);
});
Property 2: should insert all the selected tabs before the move position
for any constraints
it should move all the selected tabs before the requested position
Written with fast-check:
it("should insert all the selected tabs before the move position", () => {
fc.assert(
fc.property(
tabsWithSelectionArb(),
({ tabs, selectedTabs, movePosition }) => {
// Arrange / Act
const newTabs = reorderTabs(tabs, selectedTabs, movePosition);
// Assert
const movePositionIndex = newTabs.indexOf(movePosition);
for (const selected of selectedTabs) {
const selectedIndex = newTabs.indexOf(selected);
expect(selectedIndex).toBeLessThan(movePositionIndex);
}
}
)
);
});
Property 3: should not alter non-selected tabs
for any constraints
it should not impact other tabs (non selected ones)
Written with fast-check:
it("should not alter non-selected tabs", () => {
fc.assert(
fc.property(
tabsWithSelectionArb(),
({ tabs, selectedTabs, movePosition }) => {
// Arrange / Act
const newTabs = reorderTabs(tabs, selectedTabs, movePosition);
// Assert
expect(newTabs.filter((t) => !selectedTabs.includes(t))).toEqual(
tabs.filter((t) => !selectedTabs.includes(t))
);
}
)
);
});
Property 4: should not change the list of tabs, just its order
for any constraints
it should not change the list of tabs but just re-order them
Written with fast-check:
it("should not change the list of tabs, just its order", () => {
fc.assert(
fc.property(
tabsWithSelectionArb(),
({ tabs, selectedTabs, movePosition }) => {
// Arrange / Act
const newTabs = reorderTabs(tabs, selectedTabs, movePosition);
// Assert
expect([...newTabs].sort()).toEqual([...tabs].sort());
}
)
);
});
Back to "Advent of PBT 2021" to see topics covered during the other days and their solutions.
More about this serie on @ndubien or with the hashtag #AdventOfPBT.
Top comments (0)