DEV Community

Nicolas DUBIEN
Nicolas DUBIEN

Posted on • Edited on

Advent of PBT 2021 - Day 14 - Solution

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))
        )
      })
    );
}
Enter fullscreen mode Exit fullscreen mode

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);
      }
    )
  );
});
Enter fullscreen mode Exit fullscreen mode

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);
        }
      }
    )
  );
});
Enter fullscreen mode Exit fullscreen mode

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))
        );
      }
    )
  );
});
Enter fullscreen mode Exit fullscreen mode

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());
      }
    )
  );
});
Enter fullscreen mode Exit fullscreen mode

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)