export interface ITestCaseSubject { subject: string; } export interface IEachTestCase extends ITestCaseSubject { each: Array<() => any>; } export type ArrayMoreThanOne = [T, T, ...T[]]; export function testPermutations(subjectCases: ArrayMoreThanOne) { function traverseAll( fn: (subject: string, permutation: () => string, subjectIndex: number, permutationIndex: number) => void, ): void { for (let subjectIndex = 0; subjectIndex < subjectCases.length; subjectIndex++) { const subjectCase = subjectCases[subjectIndex]; const permutations = subjectCase.each; for (let permutationIndex = 0; permutationIndex < permutations.length; permutationIndex++) { fn(subjectCase.subject, permutations[permutationIndex], subjectIndex, permutationIndex); } } } function traverseAllBut( skipSubjectIndex: number, skipPermutationIndex: number, fn: (subject: string, permutation: () => string, subjectIndex: number, permutationIndex: number) => void, ): void { for (let subjectIndex = 0; subjectIndex < subjectCases.length; subjectIndex++) { if (subjectIndex === skipSubjectIndex) continue; const subjectCase = subjectCases[subjectIndex]; const permutations = subjectCase.each; for (let permutationIndex = 0; permutationIndex < permutations.length; permutationIndex++) { if (permutationIndex === skipPermutationIndex) continue; fn(subjectCase.subject, permutations[permutationIndex], subjectIndex, permutationIndex); } } } return (runTest: () => void | Promise) => { let whenDescription: string[]; beforeEach(() => { whenDescription = []; traverseAll((subject, permutation, subjectIndex, permutationIndex) => { traverseAllBut(subjectIndex, permutationIndex, (relatedSubject, relatedPermutation) => { whenDescription.push(`${relatedSubject} = ${JSON.stringify(relatedPermutation())}`); }); }); }); // describe(`when ${whenDescription.join(", ")} and ${subject} = ${JSON.stringify(permutation())}`, () => { // beforeEach(permutation); // runTest(); // // it("returns true", async () => { // // expect(await runTest()).toBe(true); // // }); // }); }; }