Best JavaScript code snippet using fast-check-monorepo
SchedulerImplem.spec.ts
Source:SchedulerImplem.spec.ts  
1import fc from 'fast-check';2import {3  ScheduledTask,4  SchedulerImplem,5  TaskSelector,6} from '../../../../../src/arbitrary/_internals/implementations/SchedulerImplem';7import { Scheduler } from '../../../../../src/arbitrary/_internals/interfaces/Scheduler';8import { cloneMethod, hasCloneMethod } from '../../../../../src/check/symbols';9function beforeEachHook() {10  jest.resetModules();11  jest.restoreAllMocks();12  fc.configureGlobal({ beforeEach: beforeEachHook });13}14beforeEach(beforeEachHook);15const buildUnresolved = () => {16  let resolved = false;17  let resolve = () => {};18  const p = new Promise<void>(19    (r) =>20      (resolve = () => {21        resolved = true;22        r();23      })24  );25  return { p, resolve, hasBeenResolved: () => resolved };26};27const delay = () => new Promise((r) => setTimeout(r, 0));28describe('SchedulerImplem', () => {29  describe('waitOne', () => {30    it('should throw when there is no scheduled promise in the pipe', async () => {31      // Arrange32      const act = jest.fn().mockImplementation((f) => f());33      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex: jest.fn() };34      // Act35      const s = new SchedulerImplem(act, taskSelector);36      // Assert37      await expect(s.waitOne()).rejects.toMatchInlineSnapshot(`[Error: No task scheduled]`);38    });39    it('should wrap waitOne call using act whenever specified', async () => {40      // Arrange41      const act = jest.fn().mockImplementation((f) => f());42      const nextTaskIndexLengths: number[] = [];43      const nextTaskIndex = jest.fn().mockImplementation((tasks: ScheduledTask<unknown>[]) => {44        // `tasks` pointer being re-used from one call to another (mutate)45        // we cannot rely on toHaveBeenCalledWith46        nextTaskIndexLengths.push(tasks.length);47        return 0;48      });49      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };50      // Act51      const s = new SchedulerImplem(act, taskSelector);52      s.schedule(Promise.resolve(42));53      // Assert54      expect(act).not.toHaveBeenCalled();55      expect(nextTaskIndex).not.toHaveBeenCalled();56      await s.waitOne();57      expect(act).toHaveBeenCalledTimes(1);58      expect(nextTaskIndex).toHaveBeenCalledTimes(1);59      expect(nextTaskIndexLengths).toEqual([1]); // only one task scheduled60    });61    it('should wait the end of act before resolving waitOne', async () => {62      // Arrange63      const p1 = buildUnresolved();64      const p2 = buildUnresolved();65      const act = jest.fn().mockImplementation(async (f) => {66        await p1.p;67        await f();68        await p2.p;69      });70      const nextTaskIndex = jest.fn().mockReturnValue(0);71      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };72      // Act73      let promiseResolved = false;74      let waitOneResolved = false;75      const s = new SchedulerImplem(act, taskSelector);76      s.schedule(Promise.resolve(1)).then(() => (promiseResolved = true));77      // Assert78      s.waitOne().then(() => (waitOneResolved = true));79      await delay();80      expect(promiseResolved).toBe(false);81      expect(waitOneResolved).toBe(false);82      p1.resolve();83      await delay();84      expect(promiseResolved).toBe(true);85      expect(waitOneResolved).toBe(false);86      p2.resolve();87      await delay();88      expect(promiseResolved).toBe(true);89      expect(waitOneResolved).toBe(true);90    });91  });92  describe('waitAll', () => {93    it('should not throw when there is no scheduled promise in the pipe', async () => {94      // Arrange95      const act = jest.fn().mockImplementation((f) => f());96      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex: jest.fn() };97      // Act98      const s = new SchedulerImplem(act, taskSelector);99      // Assert100      await s.waitAll();101    });102    it('should wrap waitAll call using act whenever specified', async () =>103      fc.assert(104        fc.asyncProperty(fc.infiniteStream(fc.nat()), async (seeds) => {105          // Arrange106          const act = jest.fn().mockImplementation((f) => f());107          const nextTaskIndexLengths: number[] = [];108          const nextTaskIndex = buildSeededNextTaskIndex(seeds, nextTaskIndexLengths);109          const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };110          // Act111          const s = new SchedulerImplem(act, taskSelector);112          s.schedule(Promise.resolve(1));113          s.schedule(Promise.resolve(8));114          s.schedule(Promise.resolve(42));115          // Assert116          expect(act).not.toHaveBeenCalled();117          expect(nextTaskIndex).not.toHaveBeenCalled();118          await s.waitAll();119          expect(act).toHaveBeenCalledTimes(3);120          expect(nextTaskIndex).toHaveBeenCalledTimes(3);121          expect(nextTaskIndexLengths).toEqual([3, 2, 1]);122        })123      ));124    it('should wait the end of act before moving to the next task', async () =>125      fc.assert(126        fc.asyncProperty(fc.infiniteStream(fc.nat()), async (seeds) => {127          // Arrange128          let locked = false;129          const updateLocked = (newLocked: boolean) => (locked = newLocked);130          const act = jest.fn().mockImplementation(async (f) => {131            expect(locked).toBe(false);132            updateLocked(true); // equivalent to: locked = true133            await f();134            updateLocked(false); // equivalent to: locked = false135          });136          const nextTaskIndex = buildSeededNextTaskIndex(seeds);137          const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };138          // Act139          const s = new SchedulerImplem(act, taskSelector);140          s.schedule(Promise.resolve(1));141          s.schedule(Promise.resolve(8));142          s.schedule(Promise.resolve(42));143          // Assert144          await s.waitAll();145          expect(locked).toBe(false);146        })147      ));148  });149  describe('waitFor', () => {150    it('should not release any of the scheduled promises if the task has already been resolved', async () => {151      // Arrange152      const p1 = buildUnresolved();153      const p2 = buildUnresolved();154      const nextTaskIndex = jest.fn();155      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };156      const awaitedTaskValue = Symbol();157      const awaitedTask = Promise.resolve(awaitedTaskValue);158      // Act159      const s = new SchedulerImplem((f) => f(), taskSelector);160      s.schedule(p1.p);161      s.schedule(p2.p);162      // Assert163      const value = await s.waitFor(awaitedTask);164      expect(value).toBe(awaitedTaskValue);165      expect(nextTaskIndex).not.toHaveBeenCalled();166      expect(s.count()).toBe(2); // Still two pending scheduled tasks (none released yet)167    });168    it('should stop releasing scheduled promises as soon as the task resolves', async () => {169      // Arrange170      const p1 = buildUnresolved();171      const p2 = buildUnresolved();172      const p3 = buildUnresolved();173      const nextTaskIndexParams: unknown[] = [];174      const nextTaskIndex = jest175        .fn()176        .mockImplementationOnce((scheduledTasks) => {177          nextTaskIndexParams.push([...scheduledTasks]); // need to clone it as it will be altered178          return 1;179        })180        .mockImplementationOnce((scheduledTasks) => {181          nextTaskIndexParams.push([...scheduledTasks]);182          return 0;183        });184      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };185      // Act186      const s = new SchedulerImplem((f) => f(), taskSelector);187      const sp1 = s.schedule(p1.p);188      s.schedule(p2.p);189      s.schedule(p3.p);190      const awaitedTask = sp1.then(() => Symbol());191      // Assert192      let waitForEnded = false;193      s.waitFor(awaitedTask).finally(() => (waitForEnded = true));194      await delay();195      expect(waitForEnded).toBe(false);196      expect(nextTaskIndex).toHaveBeenCalledTimes(1);197      expect(nextTaskIndexParams[0]).toEqual([198        expect.objectContaining({ original: p1.p, taskId: 1 }),199        expect.objectContaining({ original: p2.p, taskId: 2 }),200        expect.objectContaining({ original: p3.p, taskId: 3 }),201      ]);202      // nextTaskIndex returned 1, so p2 scheduled203      p2.resolve();204      await delay();205      expect(waitForEnded).toBe(false);206      expect(nextTaskIndex).toHaveBeenCalledTimes(2);207      expect(nextTaskIndexParams[1]).toEqual([208        expect.objectContaining({ original: p1.p, taskId: 1 }),209        expect.objectContaining({ original: p3.p, taskId: 3 }),210      ]);211      // nextTaskIndex returned 1, so p1 scheduled212      // We do not wait for p3 as it is not needed for the computation of awaitedTask213      p1.resolve();214      await delay();215      expect(waitForEnded).toBe(true);216      expect(nextTaskIndex).toHaveBeenCalledTimes(2); // no other call received217      expect(s.count()).toBe(1); // Still one pending scheduled task218    });219    it('should wait any released scheduled task to end even if the one we waited for resolved on its own', async () => {220      // Arrange221      const p1 = buildUnresolved();222      const pAwaited = buildUnresolved();223      const nextTaskIndex = jest.fn().mockReturnValueOnce(0);224      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };225      // Act226      const s = new SchedulerImplem((f) => f(), taskSelector);227      s.schedule(p1.p);228      // Assert229      let waitForEnded = false;230      s.waitFor(pAwaited.p).finally(() => (waitForEnded = true));231      expect(waitForEnded).toBe(false);232      expect(nextTaskIndex).not.toHaveBeenCalled(); // not called synchronously233      await delay();234      expect(waitForEnded).toBe(false);235      expect(nextTaskIndex).toHaveBeenCalledTimes(1);236      // Let's resolve waiated task237      pAwaited.resolve();238      await delay();239      expect(waitForEnded).toBe(false); // should not be visible yet as we need to wait the running one240      // Let's resolve running one241      p1.resolve();242      await delay();243      expect(waitForEnded).toBe(true);244      expect(s.count()).toBe(0); // No pending scheduled task245    });246    it('should wait and release scheduled tasks coming after the call to waitFor', async () => {247      // Arrange248      const p1 = buildUnresolved();249      const nextTaskIndex = jest.fn().mockReturnValueOnce(0);250      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };251      let resolveAwaitedTask: () => void;252      const awaitedTask = new Promise<void>((r) => (resolveAwaitedTask = r));253      // Act254      const s = new SchedulerImplem((f) => f(), taskSelector);255      // Assert256      let waitForEnded = false;257      s.waitFor(awaitedTask).finally(() => (waitForEnded = true));258      await delay();259      expect(waitForEnded).toBe(false);260      expect(nextTaskIndex).not.toHaveBeenCalled(); // nothing scheduled yet261      // Schedule p1 a requirement for our resulting task262      s.schedule(p1.p).then(() => resolveAwaitedTask());263      expect(waitForEnded).toBe(false);264      expect(nextTaskIndex).not.toHaveBeenCalled(); // no synchronous trigger265      await delay();266      expect(waitForEnded).toBe(false);267      expect(nextTaskIndex).toHaveBeenCalledTimes(1);268      // Releasing p1 should release the main task269      p1.resolve();270      await delay();271      expect(waitForEnded).toBe(true);272      expect(nextTaskIndex).toHaveBeenCalledTimes(1);273      expect(s.count()).toBe(0); // No more pending scheduled task, all have been released274    });275    it('should accept multiple tasks to be scheduled together after the call to waitFor', async () => {276      // Arrange277      const p1 = buildUnresolved();278      const p2 = buildUnresolved();279      const nextTaskIndexParams: unknown[] = [];280      const nextTaskIndex = jest.fn().mockImplementation((scheduledTasks) => {281        nextTaskIndexParams.push([...scheduledTasks]); // We need to clone it as it will be altered after the call282        return 0;283      });284      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };285      let resolveAwaitedTask: () => void;286      const awaitedTask = new Promise<void>((r) => (resolveAwaitedTask = r));287      // Act288      const s = new SchedulerImplem((f) => f(), taskSelector);289      // Assert290      let waitForEnded = false;291      s.waitFor(awaitedTask).finally(() => (waitForEnded = true));292      await delay();293      expect(waitForEnded).toBe(false);294      expect(nextTaskIndex).not.toHaveBeenCalled(); // nothing scheduled yet295      // Schedule p1 and p2, requirements for our resulting task296      Promise.all([s.schedule(p1.p), s.schedule(p2.p)]).then(() => resolveAwaitedTask());297      expect(waitForEnded).toBe(false);298      expect(nextTaskIndex).not.toHaveBeenCalled(); // no synchronous trigger299      await delay();300      expect(waitForEnded).toBe(false);301      expect(nextTaskIndex).toHaveBeenCalledTimes(1);302      expect(nextTaskIndexParams[0]).toEqual([303        expect.objectContaining({ original: p1.p }),304        expect.objectContaining({ original: p2.p }),305      ]);306      // Releasing p1 should trigger the call to schedule p2 (main not released yet)307      p1.resolve();308      await delay();309      expect(waitForEnded).toBe(false);310      expect(nextTaskIndex).toHaveBeenCalledTimes(2);311      expect(nextTaskIndexParams[1]).toEqual([expect.objectContaining({ original: p2.p })]);312      // Both p1 and p2 have been released so main has also been released313      p2.resolve();314      await delay();315      expect(waitForEnded).toBe(true);316      expect(nextTaskIndex).toHaveBeenCalledTimes(2);317      expect(s.count()).toBe(0); // No more pending scheduled task, all have been released318    });319    it('should accept multiple tasks to be scheduled together even if coming from distinct immediately resolved promises after the call to waitFor', async () => {320      // Arrange321      const p1 = Promise.resolve(1);322      const p2 = Promise.resolve(2);323      const nextTaskIndexParams: unknown[] = [];324      const nextTaskIndex = jest.fn().mockImplementation((scheduledTasks) => {325        nextTaskIndexParams.push([...scheduledTasks]); // We need to clone it as it will be altered after the call326        return 0;327      });328      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };329      let resolveAwaitedTask: () => void;330      const awaitedTask = new Promise<void>((r) => (resolveAwaitedTask = r));331      // Act332      const s = new SchedulerImplem((f) => f(), taskSelector);333      // Assert334      let waitForEnded = false;335      s.waitFor(awaitedTask).finally(() => (waitForEnded = true));336      await delay();337      expect(waitForEnded).toBe(false);338      expect(nextTaskIndex).not.toHaveBeenCalled(); // nothing scheduled yet339      // Schedule p1 and p2 in a very close futur, requirements for our resulting task340      const delayedP1Scheduling = Promise.resolve().then(() => s.schedule(p1));341      const delayedP2Scheduling = Promise.resolve().then(() => s.schedule(p2));342      Promise.all([delayedP1Scheduling, delayedP2Scheduling]).then(() => resolveAwaitedTask());343      expect(waitForEnded).toBe(false);344      expect(nextTaskIndex).not.toHaveBeenCalled(); // no synchronous trigger345      await delay();346      expect(waitForEnded).toBe(true);347      expect(nextTaskIndex).toHaveBeenCalledTimes(2);348      expect(nextTaskIndexParams[0]).toEqual([349        expect.objectContaining({ original: p1 }),350        expect.objectContaining({ original: p2 }),351      ]);352      expect(nextTaskIndexParams[1]).toEqual([expect.objectContaining({ original: p2 })]);353      expect(s.count()).toBe(0); // No more pending scheduled task, all have been released354    });355    it('should consider separately tasks scheduled via different timeouts after the call to waitFor', async () => {356      // Arrange357      const p1 = Promise.resolve(1);358      const p2 = Promise.resolve(2);359      const nextTaskIndexParams: unknown[] = [];360      const nextTaskIndex = jest.fn().mockImplementation((scheduledTasks) => {361        nextTaskIndexParams.push([...scheduledTasks]); // We need to clone it as it will be altered after the call362        return 0;363      });364      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };365      let resolveAwaitedTask: () => void;366      const awaitedTask = new Promise<void>((r) => (resolveAwaitedTask = r));367      // Act368      const s = new SchedulerImplem((f) => f(), taskSelector);369      // Assert370      let waitForEnded = false;371      s.waitFor(awaitedTask).finally(() => (waitForEnded = true));372      await delay();373      expect(waitForEnded).toBe(false);374      expect(nextTaskIndex).not.toHaveBeenCalled(); // nothing scheduled yet375      // Schedule p1 and p2 in a very close futur, requirements for our resulting task376      const delayedP1Scheduling = delay().then(() => s.schedule(p1));377      const delayedP2Scheduling = delay().then(() => s.schedule(p2));378      Promise.all([delayedP1Scheduling, delayedP2Scheduling]).then(() => resolveAwaitedTask());379      expect(waitForEnded).toBe(false);380      expect(nextTaskIndex).not.toHaveBeenCalled(); // no synchronous trigger381      await delay();382      expect(waitForEnded).toBe(true);383      expect(nextTaskIndex).toHaveBeenCalledTimes(2);384      expect(nextTaskIndexParams[0]).toEqual([expect.objectContaining({ original: p1 })]);385      expect(nextTaskIndexParams[1]).toEqual([expect.objectContaining({ original: p2 })]);386      expect(s.count()).toBe(0); // No more pending scheduled task, all have been released387    });388    it('should forward exception thrown by the awaited task', async () => {389      // Arrange390      const nextTaskIndex = jest.fn();391      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };392      const awaitedTaskValue = new Error('thrown by awaited');393      const awaitedTask = Promise.reject(awaitedTaskValue);394      // Act395      const s = new SchedulerImplem((f) => f(), taskSelector);396      // Assert397      await expect(() => s.waitFor(awaitedTask)).rejects.toThrow(awaitedTaskValue);398    });399    it('should be able to wait for a task being itself a scheduled one (no priority to release it first)', async () => {400      // Arrange401      const p1 = Promise.resolve(1);402      const p2 = Promise.resolve(2);403      const nextTaskIndex = jest.fn().mockReturnValueOnce(0).mockReturnValueOnce(1);404      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };405      const awaitedTaskValue = Symbol();406      const awaitedTask = Promise.resolve(awaitedTaskValue);407      // Act408      const s = new SchedulerImplem((f) => f(), taskSelector);409      s.schedule(p1);410      s.schedule(p2);411      // Assert412      const value = await s.waitFor(s.schedule(awaitedTask));413      expect(value).toBe(awaitedTaskValue);414      expect(nextTaskIndex).toHaveBeenCalledTimes(2);415      expect(s.count()).toBe(1); // Still one pending scheduled task (only p1 has been released before the one we waited for)416    });417    it('should not release multiple scheduled tasks at the same time even if called via multiple waitFor', async () => {418      // Arrange419      const p1 = buildUnresolved();420      const p2 = buildUnresolved();421      const p3 = buildUnresolved();422      const p4 = buildUnresolved();423      const nextTaskIndex = jest424        .fn()425        .mockReturnValueOnce(1) // releasing p2 in [p1,p2,p3,p4]426        .mockReturnValueOnce(2) // releasing p4 in [p1,p3,p4]427        .mockReturnValueOnce(0); // releasing p1 in [p1,p4]428      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };429      // Act430      const s = new SchedulerImplem((f) => f(), taskSelector);431      const sp1 = s.schedule(p1.p);432      const sp2 = s.schedule(p2.p);433      s.schedule(p3.p);434      s.schedule(p4.p);435      const awaitedTask1 = sp1.then(() => Symbol());436      const awaitedTask2 = sp2.then(() => Symbol());437      // Assert438      let waitForEnded1 = false;439      let waitForEnded2 = false;440      s.waitFor(awaitedTask1).finally(() => (waitForEnded1 = true));441      s.waitFor(awaitedTask2).finally(() => (waitForEnded2 = true));442      await delay();443      expect(waitForEnded1).toBe(false);444      expect(waitForEnded2).toBe(false);445      expect(nextTaskIndex).toHaveBeenCalledTimes(1);446      // p2 released, let's resolve it447      p2.resolve();448      await delay();449      expect(waitForEnded1).toBe(false);450      expect(waitForEnded2).toBe(true);451      expect(nextTaskIndex).toHaveBeenCalledTimes(2);452      // p4 released, let's resolve it453      p4.resolve();454      await delay();455      expect(waitForEnded1).toBe(false);456      expect(waitForEnded2).toBe(true);457      expect(nextTaskIndex).toHaveBeenCalledTimes(3);458      // p1 released, let's resolve it459      p1.resolve();460      await delay();461      expect(waitForEnded1).toBe(true);462      expect(waitForEnded2).toBe(true);463      expect(nextTaskIndex).toHaveBeenCalledTimes(3); // no other call received464      expect(s.count()).toBe(1); // Still one pending scheduled task for p3465    });466    it('should end whenever possible while never launching multiple tasks at the same time', async () => {467      const schedulingTypeArb = fc.constantFrom(...(['none', 'init'] as const));468      const dependenciesArbFor = (currentItem: number) =>469        fc.uniqueArray(fc.nat({ max: currentItem - 2 }), { maxLength: currentItem - 1 });470      const buildAndAddScheduled = (471        s: SchedulerImplem<unknown>,472        unscheduled: Promise<unknown>,473        allPs: Promise<unknown>[],474        dependencies: number[],475        schedulingType: 'none' | 'init'476      ) => {477        const label = `p${allPs.length + 1}:${JSON.stringify(dependencies)}`;478        const deps = dependencies.map((id) => allPs[id]);479        let self: Promise<unknown>;480        if (schedulingType === 'init') {481          if (deps.length === 0) {482            self = s.schedule(unscheduled, label);483          } else {484            self = Promise.all(deps).then(() => s.schedule(unscheduled, label));485          }486        } else {487          self = deps.length !== 0 ? Promise.all([...deps, unscheduled]) : unscheduled;488        }489        allPs.push(self);490      };491      const scheduleResolution = (492        wrappingScheduler: fc.Scheduler<unknown>,493        p: ReturnType<typeof buildUnresolved>,494        pName: string,495        directResolved: boolean,496        ctx: fc.ContextValue497      ): void => {498        if (directResolved) {499          ctx.log(`${pName} resolved (direct)`);500          p.resolve();501          return;502        }503        wrappingScheduler.schedule(Promise.resolve(`Resolve ${pName}`)).then(() => {504          ctx.log(`${pName} resolved`);505          p.resolve();506        });507      };508      await fc.assert(509        fc.asyncProperty(510          // Scheduler not being tested itself, it will be used to schedule when task will resolve511          fc.scheduler(),512          // Stream of positive integers used to tell which task should be selected by `nextTaskIndex`513          // whenever asked for the next task to be scheduled514          fc.infiniteStream(fc.nat()),515          // Define the tree of pre-requisite tasks:516          // - type of scheduling: should they be scheduled within the Schduler under test?517          // - when should they resolved: on init or when `fc.scheduler` decides it?518          // - what are the dependencies needed for this task?519          fc.tuple(schedulingTypeArb, fc.boolean()),520          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(2)),521          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(3)),522          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(4)),523          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(5)),524          fc.tuple(fc.boolean(), dependenciesArbFor(6)),525          fc.tuple(fc.boolean(), dependenciesArbFor(6)),526          fc.tuple(fc.boolean(), dependenciesArbFor(6)),527          // Extra boolean values used to add some delays between resolved Promises and next ones528          fc.infiniteStream(fc.boolean()),529          // Logger for easier troubleshooting530          fc.context(),531          async (532            wrappingScheduler,533            nextTaskIndexSeed,534            [schedulingType1, directResolve1],535            [schedulingType2, directResolve2, dependencies2],536            [schedulingType3, directResolve3, dependencies3],537            [schedulingType4, directResolve4, dependencies4],538            [schedulingType5, directResolve5, dependencies5],539            [directResolveA, finalDependenciesA],540            [directResolveB, finalDependenciesB],541            [directResolveC, finalDependenciesC],542            addExtraDelays,543            ctx544          ) => {545            // Arrange546            const p1 = buildUnresolved();547            const p2 = buildUnresolved();548            const p3 = buildUnresolved();549            const p4 = buildUnresolved();550            const p5 = buildUnresolved();551            const rawAllPs = [p1, p2, p3, p4, p5];552            const pAwaitedA = buildUnresolved();553            const pAwaitedB = buildUnresolved();554            const pAwaitedC = buildUnresolved();555            let unknownTaskReleased = false;556            let multipleTasksReleasedAtTheSameTime: string | undefined = undefined;557            let alreadyRunningPx: typeof p1 | undefined = undefined;558            const taskSelector: TaskSelector<unknown> = {559              clone: jest.fn(),560              nextTaskIndex: (scheduledTasks) => {561                const selectedId = nextTaskIndexSeed.next().value % scheduledTasks.length;562                const selectedPx = rawAllPs.find((p) => p.p === scheduledTasks[selectedId].original);563                const newPx = `p${rawAllPs.indexOf(selectedPx!) + 1}`;564                ctx.log(`Releasing ${newPx}${selectedPx!.hasBeenResolved() ? ' (resolved)' : ''}`);565                if (566                  multipleTasksReleasedAtTheSameTime === undefined &&567                  alreadyRunningPx !== undefined &&568                  !alreadyRunningPx.hasBeenResolved()569                ) {570                  const oldPx = `p${rawAllPs.indexOf(alreadyRunningPx) + 1}`;571                  multipleTasksReleasedAtTheSameTime = `${oldPx} already running when releasing ${newPx}`;572                }573                alreadyRunningPx = selectedPx;574                unknownTaskReleased = unknownTaskReleased || alreadyRunningPx === undefined;575                return selectedId;576              },577            };578            const s = new SchedulerImplem((f) => f(), taskSelector);579            const allPs: Promise<unknown>[] = [];580            buildAndAddScheduled(s, p1.p, allPs, [], schedulingType1);581            buildAndAddScheduled(s, p2.p, allPs, dependencies2, schedulingType2);582            buildAndAddScheduled(s, p3.p, allPs, dependencies3, schedulingType3);583            buildAndAddScheduled(s, p4.p, allPs, dependencies4, schedulingType4);584            buildAndAddScheduled(s, p5.p, allPs, dependencies5, schedulingType5);585            const awaitedTaskA = Promise.all([...finalDependenciesA.map((id) => allPs[id]), pAwaitedA.p]);586            let resolvedA = false;587            s.waitFor(awaitedTaskA).then(() => {588              ctx.log(`A ended`);589              resolvedA = true;590            });591            const awaitedTaskB = Promise.all([...finalDependenciesB.map((id) => allPs[id]), pAwaitedB.p]);592            let resolvedB = false;593            s.waitFor(awaitedTaskB).then(() => {594              ctx.log(`B ended`);595              resolvedB = true;596            });597            const awaitedTaskC = Promise.all([...finalDependenciesC.map((id) => allPs[id]), pAwaitedC.p]);598            let resolvedC = false;599            s.waitFor(awaitedTaskC).then(() => {600              ctx.log(`C ended`);601              resolvedC = true;602            });603            // Act604            scheduleResolution(wrappingScheduler, p1, 'p1', directResolve1, ctx);605            scheduleResolution(wrappingScheduler, p2, 'p2', directResolve2, ctx);606            scheduleResolution(wrappingScheduler, p3, 'p3', directResolve3, ctx);607            scheduleResolution(wrappingScheduler, p4, 'p4', directResolve4, ctx);608            scheduleResolution(wrappingScheduler, p5, 'p5', directResolve5, ctx);609            scheduleResolution(wrappingScheduler, pAwaitedA, 'pAwaitedA', directResolveA, ctx);610            scheduleResolution(wrappingScheduler, pAwaitedB, 'pAwaitedB', directResolveB, ctx);611            scheduleResolution(wrappingScheduler, pAwaitedC, 'pAwaitedC', directResolveC, ctx);612            while (wrappingScheduler.count() > 0) {613              // Extra delays based on timeouts of 0ms can potentially trigger unwanted bugs: let's try to add some before waitOne.614              if (addExtraDelays.next().value) {615                await delay();616              }617              await wrappingScheduler.waitOne();618            }619            // Extra delay done after all the scheduling as wrappingScheduler only schedules triggers to resolve tasks620            // and never waits for their associated Promises to really resolve.621            await delay();622            // Assert623            // All awaited tasks should have resolved624            expect(resolvedA).toBe(true);625            expect(resolvedB).toBe(true);626            expect(resolvedC).toBe(true);627            // Only one scheduled task awaited by the scheduler at a given point in time628            expect(multipleTasksReleasedAtTheSameTime).toBe(undefined);629            // Only known tasks could be scheduled630            expect(unknownTaskReleased).toBe(false);631          }632        )633      );634    });635  });636  describe('schedule', () => {637    it('should postpone completion of promise but call it with right parameters in case of success', async () => {638      // Arrange639      const expectedThenValue = 123;640      const thenFunction = jest.fn();641      const act = jest.fn().mockImplementation((f) => f());642      const nextTaskIndexLengths: number[] = [];643      const nextTaskIndex = jest.fn().mockImplementationOnce((tasks: ScheduledTask<unknown>[]) => {644        nextTaskIndexLengths.push(tasks.length); // tasks are mutated, toHaveBeenCalledWith cannot be used645        return 0;646      });647      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };648      // Act649      const s = new SchedulerImplem(act, taskSelector);650      s.schedule(Promise.resolve(expectedThenValue)).then(thenFunction);651      // Assert652      expect(s.count()).toBe(1);653      expect(thenFunction).not.toHaveBeenCalled();654      await s.waitOne();655      expect(thenFunction).toHaveBeenCalled();656      expect(thenFunction).toHaveBeenCalledTimes(1);657      expect(thenFunction).toHaveBeenCalledWith(expectedThenValue);658      expect(act).toHaveBeenCalledTimes(1);659      expect(nextTaskIndex).toHaveBeenCalledTimes(1);660      expect(nextTaskIndexLengths).toEqual([1]); // only one task scheduled661    });662    it('should postpone completion of promise but call it with right parameters in case of failure', async () => {663      // Arrange664      const expectedThenValue = 123;665      const catchFunction = jest.fn();666      const act = jest.fn().mockImplementation((f) => f());667      const nextTaskIndex = jest.fn().mockReturnValue(0);668      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };669      // Act670      const s = new SchedulerImplem(act, taskSelector);671      s.schedule(Promise.reject(expectedThenValue)).then(() => {}, catchFunction);672      // Assert673      expect(s.count()).toBe(1);674      expect(catchFunction).not.toHaveBeenCalled();675      await s.waitOne();676      expect(catchFunction).toHaveBeenCalled();677      expect(catchFunction).toHaveBeenCalledTimes(1);678      expect(catchFunction).toHaveBeenCalledWith(expectedThenValue);679    });680    it('should be able to schedule multiple promises', async () => {681      // Arrange682      const tasks = [Promise.resolve(1), Promise.resolve(8), Promise.resolve(2)];683      const act = jest.fn().mockImplementation((f) => f());684      const nextTaskIndex = jest685        .fn()686        .mockImplementationOnce((scheduledTasks) => {687          expect(scheduledTasks).toEqual([688            expect.objectContaining({ original: tasks[0] }), // selected as nextTaskIndex returns 0689            expect.objectContaining({ original: tasks[1] }),690            expect.objectContaining({ original: tasks[2] }),691          ]);692          return 0;693        })694        .mockImplementationOnce((scheduledTasks) => {695          expect(scheduledTasks).toEqual([696            expect.objectContaining({ original: tasks[1] }),697            expect.objectContaining({ original: tasks[2] }), // selected as nextTaskIndex returns 1698          ]);699          return 1;700        })701        .mockImplementationOnce((scheduledTasks) => {702          expect(scheduledTasks).toEqual([703            expect.objectContaining({ original: tasks[1] }), // selected as nextTaskIndex returns 0704          ]);705          return 0;706        });707      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };708      // Act709      const s = new SchedulerImplem(act, taskSelector);710      for (const t of tasks) {711        s.schedule(t);712      }713      // Assert714      expect(s.count()).toBe(tasks.length);715      await s.waitAll();716      expect(s.count()).toBe(0);717      expect(act).toHaveBeenCalledTimes(3);718      expect(nextTaskIndex).toHaveBeenCalledTimes(3);719    });720    it('should be able to waitAll promises scheduling others', async () =>721      fc.assert(722        fc.asyncProperty(fc.infiniteStream(fc.nat()), async (seeds) => {723          // Arrange724          const status = { done: false };725          const nothingResolved = {726            1: false,727            2: false,728            3: false,729            4: false,730            5: false,731          };732          const resolved = { ...nothingResolved };733          const act = jest.fn().mockImplementation((f) => f());734          const nextTaskIndex = buildSeededNextTaskIndex(seeds);735          const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };736          // Act737          const s = new SchedulerImplem(act, taskSelector);738          s.schedule(Promise.resolve(1)).then(() => {739            resolved[1] = true;740            Promise.all([741              s.schedule(Promise.resolve(2)).then(() => {742                resolved[2] = true;743              }),744              s.schedule(Promise.resolve(3)).then(() => {745                resolved[3] = true;746                s.schedule(Promise.resolve(4)).then(() => {747                  resolved[4] = true;748                });749              }),750            ]).then(() => {751              s.schedule(Promise.resolve(5)).then(() => {752                resolved[5] = true;753                status.done = true;754              });755            });756          });757          // Assert758          expect(status.done).toBe(false);759          expect(resolved).toEqual(nothingResolved);760          await s.waitAll();761          expect(status.done).toBe(true);762          expect(resolved).toEqual({763            1: true,764            2: true,765            3: true,766            4: true,767            5: true,768          });769        })770      ));771    it('should show both resolved, rejected and pending promises in toString', async () => {772      // Arrange773      const act = jest.fn().mockImplementation((f) => f());774      const nextTaskIndex = jest775        .fn()776        .mockReturnValueOnce(5) // task#6 resolved, state was: [0,1,2,3,4,5,6,7,8,9]777        .mockReturnValueOnce(5) // task#7 resolved, state was: [0,1,2,3,4,6,7,8,9]778        .mockReturnValueOnce(1) // task#2 resolved, state was: [0,1,2,3,4,7,8,9]779        .mockReturnValueOnce(0) // task#1 resolved, state was: [0,2,3,4,7,8,9]780        .mockReturnValueOnce(1) // task#4 resolved, state was: [2,3,4,7,8,9]781        .mockReturnValueOnce(2) // task#8 resolved, state was: [2,4,7,8,9]782        .mockReturnValueOnce(3) // task#10 resolved, state was: [2,4,8,9]783        .mockReturnValueOnce(0) // task#3 resolved, state was: [2,4,8]784        .mockReturnValueOnce(0) // task#5 resolved, state was: [4,8]785        .mockReturnValueOnce(0); // task#9 resolved, state was: [8]786      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };787      // Act788      const s = new SchedulerImplem(act, taskSelector);789      for (let idx = 0; idx !== 10; ++idx) {790        if (idx % 2 === 0) s.schedule(Promise.resolve(idx));791        else s.schedule(Promise.reject(idx));792      }793      // Assert794      expect(s.count()).toBe(10);795      expect(s.toString()).toMatchInlineSnapshot(`796          "schedulerFor()\`797          -> [task\${1}] promise pending798          -> [task\${2}] promise pending799          -> [task\${3}] promise pending800          -> [task\${4}] promise pending801          -> [task\${5}] promise pending802          -> [task\${6}] promise pending803          -> [task\${7}] promise pending804          -> [task\${8}] promise pending805          -> [task\${9}] promise pending806          -> [task\${10}] promise pending\`"807        `);808      await s.waitOne();809      await s.waitOne();810      await s.waitOne();811      expect(s.toString()).toMatchInlineSnapshot(`812          "schedulerFor()\`813          -> [task\${6}] promise rejected with value 5814          -> [task\${7}] promise resolved with value 6815          -> [task\${2}] promise rejected with value 1816          -> [task\${1}] promise pending817          -> [task\${3}] promise pending818          -> [task\${4}] promise pending819          -> [task\${5}] promise pending820          -> [task\${8}] promise pending821          -> [task\${9}] promise pending822          -> [task\${10}] promise pending\`"823        `);824      await s.waitOne();825      await s.waitOne();826      await s.waitOne();827      expect(s.toString()).toMatchInlineSnapshot(`828          "schedulerFor()\`829          -> [task\${6}] promise rejected with value 5830          -> [task\${7}] promise resolved with value 6831          -> [task\${2}] promise rejected with value 1832          -> [task\${1}] promise resolved with value 0833          -> [task\${4}] promise rejected with value 3834          -> [task\${8}] promise rejected with value 7835          -> [task\${3}] promise pending836          -> [task\${5}] promise pending837          -> [task\${9}] promise pending838          -> [task\${10}] promise pending\`"839        `);840      await s.waitOne();841      await s.waitOne();842      await s.waitOne();843      await s.waitOne();844      expect(s.toString()).toMatchInlineSnapshot(`845          "schedulerFor()\`846          -> [task\${6}] promise rejected with value 5847          -> [task\${7}] promise resolved with value 6848          -> [task\${2}] promise rejected with value 1849          -> [task\${1}] promise resolved with value 0850          -> [task\${4}] promise rejected with value 3851          -> [task\${8}] promise rejected with value 7852          -> [task\${10}] promise rejected with value 9853          -> [task\${3}] promise resolved with value 2854          -> [task\${5}] promise resolved with value 4855          -> [task\${9}] promise resolved with value 8\`"856        `);857      expect(s.count()).toBe(0);858    });859    it('should properly replay schedule on cloned instance', async () => {860      // Arrange861      const promises = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];862      const then1Function = jest.fn();863      const then2Function = jest.fn();864      const act = jest.fn().mockImplementation((f) => f());865      const nextTaskIndex1 = jest.fn().mockReturnValueOnce(2).mockReturnValueOnce(1).mockReturnValueOnce(0);866      const nextTaskIndex2 = jest.fn().mockReturnValueOnce(2).mockReturnValueOnce(1).mockReturnValueOnce(0);867      const taskSelector2: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex: nextTaskIndex2 };868      const taskSelector1: TaskSelector<unknown> = {869        clone: jest.fn().mockReturnValueOnce(taskSelector2),870        nextTaskIndex: nextTaskIndex1,871      };872      // Act873      const s1 = new SchedulerImplem(act, taskSelector1);874      for (const p of promises) {875        s1.schedule(p).then(then1Function);876      }877      await s1.waitAll();878      if (!hasCloneMethod(s1)) {879        throw new Error('Expected s1 to be cloneable');880      }881      const s2 = s1[cloneMethod]();882      for (const p of promises) {883        s2.schedule(p).then(then2Function);884      }885      await s2.waitAll();886      // Assert887      expect(then1Function.mock.calls).toEqual(then2Function.mock.calls);888    });889    it('should attach passed metadata into the report', async () => {890      // Arrange891      const expectedMetadata = Symbol('123');892      const act = jest.fn().mockImplementation((f) => f());893      const nextTaskIndex = jest.fn().mockReturnValue(0);894      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };895      // Act896      const s = new SchedulerImplem(act, taskSelector);897      s.schedule(Promise.resolve(), 'label', expectedMetadata);898      // Assert899      await s.waitAll();900      const report = s.report();901      expect(report).toHaveLength(1);902      expect(report[0].status).toBe('resolved');903      expect(report[0].metadata).toBe(expectedMetadata);904    });905    it('should attach passed metadata into the report even if not executed', async () => {906      // Arrange907      const expectedMetadata = Symbol('123');908      const act = jest.fn().mockImplementation((f) => f());909      const nextTaskIndex = jest.fn().mockReturnValue(0);910      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };911      // Act912      const s = new SchedulerImplem(act, taskSelector);913      s.schedule(Promise.resolve(), 'label', expectedMetadata);914      // Assert915      const report = s.report();916      expect(report).toHaveLength(1);917      expect(report[0].status).toBe('pending');918      expect(report[0].metadata).toBe(expectedMetadata);919    });920    type ExecutionPlan = { name: string; children: ExecutionPlan[] };921    it('should be able to schedule new tasks from other tasks and wait them all with waitAll', async () =>922      fc.assert(923        fc.asyncProperty(924          fc.array(925            fc.letrec((tie) => ({926              self: fc.record({927                name: fc.hexaString({ minLength: 4, maxLength: 4 }).noBias(),928                children: fc.oneof(929                  fc.constant<ExecutionPlan[]>([]),930                  fc.array(tie('self') as fc.Arbitrary<ExecutionPlan>)931                ),932              }),933            })).self,934            { minLength: 1 }935          ),936          fc.infiniteStream(fc.nat()),937          async (plan, seeds) => {938            // Arrange939            const act = jest.fn().mockImplementation((f) => f());940            const nextTaskIndex = buildSeededNextTaskIndex(seeds);941            const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };942            const computeTasksInPlan = (tasks: ExecutionPlan[]) => {943              let count = 0;944              for (const t of tasks) {945                count += 1 + computeTasksInPlan(t.children);946              }947              return count;948            };949            const schedulePlan = (s: Scheduler, tasks: ExecutionPlan[]) => {950              for (const t of tasks) {951                s.schedule(Promise.resolve(t.name), t.name).then(() => schedulePlan(s, t.children));952              }953            };954            // Act955            const s = new SchedulerImplem(act, taskSelector);956            schedulePlan(s, plan);957            // Assert958            await s.waitAll();959            expect(s.count()).toBe(0);960            expect(s.report()).toHaveLength(computeTasksInPlan(plan));961          }962        )963      ));964  });965  describe('scheduleFunction', () => {966    it('should schedule a new promise when calling a scheduled function', async () => {967      // Arrange968      const firstCallInput = 1;969      const expectedThenValue = 123;970      const thenFunction = jest.fn();971      const act = jest.fn().mockImplementation((f) => f());972      const nextTaskIndex = jest.fn().mockReturnValue(0);973      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };974      // Act975      const s = new SchedulerImplem(act, taskSelector);976      const scheduledFun = s.scheduleFunction(async (id) => id + expectedThenValue);977      // Assert978      expect(s.count()).toBe(0);979      expect(thenFunction).not.toHaveBeenCalled();980      scheduledFun(firstCallInput).then(thenFunction);981      expect(s.count()).toBe(1);982      expect(thenFunction).not.toHaveBeenCalled();983      await s.waitOne();984      expect(thenFunction).toHaveBeenCalled();985      expect(thenFunction).toHaveBeenCalledTimes(1);986      expect(thenFunction).toHaveBeenCalledWith(firstCallInput + expectedThenValue);987    });988    it('should be able to call a scheduled function multiple times', async () => {989      // Arrange990      const firstCallInput = 1;991      const secondCallInput = 10;992      const expectedThenValue = 123;993      const thenFunction = jest.fn();994      const then2Function = jest.fn();995      const act = jest.fn().mockImplementation((f) => f());996      const nextTaskIndex = jest997        .fn()998        .mockReturnValueOnce(1) // resolving then2Function first999        .mockReturnValueOnce(0); // resolving thenFunction second1000      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1001      // Act1002      const s = new SchedulerImplem(act, taskSelector);1003      const scheduledFun = s.scheduleFunction(async (id) => id + expectedThenValue);1004      // Assert1005      expect(s.count()).toBe(0);1006      scheduledFun(firstCallInput).then(thenFunction);1007      scheduledFun(secondCallInput).then(then2Function);1008      expect(s.count()).toBe(2);1009      expect(thenFunction).not.toHaveBeenCalled();1010      expect(then2Function).not.toHaveBeenCalled();1011      await s.waitAll();1012      expect(thenFunction).toHaveBeenCalledWith(firstCallInput + expectedThenValue);1013      expect(then2Function).toHaveBeenCalledWith(secondCallInput + expectedThenValue);1014    });1015    it('should be able to waitAll for a scheduled function calling itself', async () => {1016      // Arrange1017      const firstCallInput = 10;1018      const thenFunction = jest.fn();1019      const thenImplem = (remaining: number) => {1020        thenFunction();1021        if (remaining <= 0) return;1022        scheduledFun(remaining - 1).then(thenImplem);1023      };1024      const act = jest.fn().mockImplementation((f) => f());1025      const nextTaskIndex = jest.fn().mockReturnValue(0);1026      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1027      // Act1028      const s = new SchedulerImplem(act, taskSelector);1029      const scheduledFun = s.scheduleFunction(async (id) => id);1030      // Assert1031      expect(s.count()).toBe(0);1032      scheduledFun(firstCallInput).then(thenImplem);1033      await s.waitAll();1034      expect(thenFunction).toHaveBeenCalledTimes(firstCallInput + 1);1035    });1036    it('should show both resolved, rejected and pending promises in toString', async () => {1037      // Arrange1038      const calls: [number, number][] = [1039        [0, 3],1040        [1, 4],1041        [6, 0],1042      ];1043      const act = jest.fn().mockImplementation((f) => f());1044      const nextTaskIndex = jest1045        .fn()1046        .mockReturnValueOnce(2) // task#3 resolved, state was: [0,1,2]1047        .mockReturnValueOnce(0) // task#1 resolved, state was: [0,1]1048        .mockReturnValueOnce(0); // task#2 resolved, state was: [1]1049      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1050      // Act1051      const s = new SchedulerImplem(act, taskSelector);1052      const scheduledFun = s.scheduleFunction(async (a: number, b: number) => {1053        if (a >= b) throw new Error(`Unexpected: ${a} >= ${b}`);1054        return a;1055      });1056      for (const ins of calls) {1057        scheduledFun(...ins);1058      }1059      // Assert1060      expect(s.count()).toBe(calls.length);1061      expect(s.toString()).toMatchInlineSnapshot(`1062          "schedulerFor()\`1063          -> [task\${1}] function::(0,3) pending1064          -> [task\${2}] function::(1,4) pending1065          -> [task\${3}] function::(6,0) pending\`"1066        `);1067      await s.waitOne();1068      await s.waitOne();1069      expect(s.toString()).toMatchInlineSnapshot(`1070        "schedulerFor()\`1071        -> [task\${3}] function::(6,0) rejected with value new Error("Unexpected: 6 >= 0")1072        -> [task\${1}] function::(0,3) resolved with value 01073        -> [task\${2}] function::(1,4) pending\`"1074      `);1075      await s.waitOne();1076      expect(s.toString()).toMatchInlineSnapshot(`1077        "schedulerFor()\`1078        -> [task\${3}] function::(6,0) rejected with value new Error("Unexpected: 6 >= 0")1079        -> [task\${1}] function::(0,3) resolved with value 01080        -> [task\${2}] function::(1,4) resolved with value 1\`"1081      `);1082      expect(s.count()).toBe(0);1083    });1084    it('should show function name if any in toString', async () => {1085      // Arrange1086      const act = jest.fn().mockImplementation((f) => f());1087      const nextTaskIndex = jest1088        .fn()1089        .mockReturnValueOnce(2) // task#3 resolved, state was: [0,1,2]1090        .mockReturnValueOnce(0) // task#1 resolved, state was: [0,1]1091        .mockReturnValueOnce(0); // task#2 resolved, state was: [1]1092      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1093      // Act1094      const s = new SchedulerImplem(act, taskSelector);1095      s.scheduleFunction(async function taskA() {1096        return { response: 'dummy response for task A' };1097      })();1098      s.scheduleFunction(async function anotherTaskNameForB(_input: number) {1099        return 3;1100      })(42);1101      s.scheduleFunction(async function somethingElseForC(_complexInstance: any, _anotherInput: number) {1102        return 'c';1103      })({ a: { b: 5 }, c: 0 }, 4);1104      // Assert1105      expect(s.count()).toBe(3);1106      expect(s.toString()).toMatchInlineSnapshot(`1107          "schedulerFor()\`1108          -> [task\${1}] function::taskA() pending1109          -> [task\${2}] function::anotherTaskNameForB(42) pending1110          -> [task\${3}] function::somethingElseForC({"a":{"b":5},"c":0},4) pending\`"1111        `);1112      await s.waitAll();1113      expect(s.toString()).toMatchInlineSnapshot(`1114          "schedulerFor()\`1115          -> [task\${3}] function::somethingElseForC({"a":{"b":5},"c":0},4) resolved with value "c"1116          -> [task\${1}] function::taskA() resolved with value {"response":"dummy response for task A"}1117          -> [task\${2}] function::anotherTaskNameForB(42) resolved with value 3\`"1118        `);1119      expect(s.count()).toBe(0);1120    });1121  });1122  describe('scheduleSequence', () => {1123    it('should accept empty sequences', async () => {1124      // Arrange1125      const act = jest.fn().mockImplementation((f) => f());1126      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex: jest.fn() };1127      // Act1128      const s = new SchedulerImplem(act, taskSelector);1129      const status = s.scheduleSequence([]);1130      // Assert1131      expect(s.count()).toBe(0);1132      expect(status.done).toBe(true);1133      expect(status.faulty).toBe(false);1134    });1135    it('should consider a sequence as a serie of tasks and not parallel tasks', async () => {1136      // Arrange1137      const p1Builder = jest.fn().mockResolvedValue(1);1138      const p2Builder = jest.fn().mockResolvedValue(2);1139      const p3Builder = jest.fn().mockResolvedValue(3);1140      const p4Builder = jest.fn().mockResolvedValue(4);1141      const act = jest.fn().mockImplementation((f) => f());1142      const nextTaskIndex = jest.fn().mockReturnValue(0);1143      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1144      // Act1145      const s = new SchedulerImplem(act, taskSelector);1146      s.scheduleSequence([1147        { builder: p1Builder, label: 'p1' },1148        { builder: p2Builder, label: 'p2' },1149        { builder: p3Builder, label: 'p3' },1150        { builder: p4Builder, label: 'p4' },1151      ]);1152      // Assert1153      expect(s.count()).toBe(1);1154      await s.waitAll();1155      expect(s.count()).toBe(0);1156    });1157    it('should mark schedule as done at the end of the sequence but not faulty', async () => {1158      // Arrange1159      const p1Builder = jest.fn().mockResolvedValue(1);1160      const p2Builder = jest.fn().mockResolvedValue(2);1161      const p3Builder = jest.fn().mockResolvedValue(3);1162      const p4Builder = jest.fn().mockResolvedValue(4);1163      const act = jest.fn().mockImplementation((f) => f());1164      const nextTaskIndex = jest.fn().mockReturnValue(0);1165      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1166      // Act1167      const s = new SchedulerImplem(act, taskSelector);1168      const status = s.scheduleSequence([1169        { builder: p1Builder, label: 'p1' },1170        { builder: p2Builder, label: 'p2' },1171        { builder: p3Builder, label: 'p3' },1172        { builder: p4Builder, label: 'p4' },1173      ]);1174      // Assert1175      while (s.count() > 0) {1176        expect(status.done).toBe(false);1177        expect(status.faulty).toBe(false);1178        await s.waitOne();1179      }1180      expect(status.done).toBe(true);1181      expect(status.faulty).toBe(false);1182    });1183    it('should mark faulty schedule as not done but as faulty', async () => {1184      // Arrange1185      const p1Builder = jest.fn().mockResolvedValue(1);1186      const p2Builder = jest.fn().mockResolvedValue(2);1187      const p3Builder = jest.fn().mockRejectedValue(3);1188      const p4Builder = jest.fn().mockResolvedValue(4);1189      const act = jest.fn().mockImplementation((f) => f());1190      const nextTaskIndex = jest.fn().mockReturnValue(0);1191      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1192      // Act1193      const s = new SchedulerImplem(act, taskSelector);1194      const status = s.scheduleSequence([1195        { builder: p1Builder, label: 'p1' },1196        { builder: p2Builder, label: 'p2' },1197        { builder: p3Builder, label: 'p3' },1198        { builder: p4Builder, label: 'p4' },1199      ]);1200      // Assert1201      while (s.count() > 0) {1202        expect(status.done).toBe(false);1203        await s.waitOne();1204      }1205      expect(status.done).toBe(false);1206      expect(status.faulty).toBe(true);1207    });1208    it('should execute schedule up to the first faulty task', async () => {1209      // Arrange1210      const p1Builder = jest.fn().mockResolvedValue(1);1211      const p2Builder = jest.fn().mockResolvedValue(2);1212      const p3Builder = jest.fn().mockRejectedValue(3);1213      const p4Builder = jest.fn().mockResolvedValue(4);1214      const act = jest.fn().mockImplementation((f) => f());1215      const nextTaskIndex = jest.fn().mockReturnValue(0);1216      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1217      // Act1218      const s = new SchedulerImplem(act, taskSelector);1219      s.scheduleSequence([1220        { builder: p1Builder, label: 'p1' },1221        { builder: p2Builder, label: 'p2' },1222        { builder: p3Builder, label: 'p3' },1223        { builder: p4Builder, label: 'p4' },1224      ]);1225      // Assert1226      await s.waitAll();1227      expect(p1Builder).toHaveBeenCalled();1228      expect(p2Builder).toHaveBeenCalled();1229      expect(p3Builder).toHaveBeenCalled();1230      expect(p4Builder).not.toHaveBeenCalled();1231    });1232    it('should execute sequence in order', async () => {1233      // Arrange1234      const p1Builder = jest.fn().mockResolvedValue(1);1235      const p2Builder = jest.fn().mockResolvedValue(2);1236      const p3Builder = jest.fn().mockResolvedValue(3);1237      const p4Builder = jest.fn().mockResolvedValue(4);1238      const act = jest.fn().mockImplementation((f) => f());1239      const nextTaskIndex = jest.fn().mockReturnValue(0);1240      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1241      // Act1242      const s = new SchedulerImplem(act, taskSelector);1243      s.scheduleSequence([1244        { builder: p1Builder, label: 'p1' },1245        { builder: p2Builder, label: 'p2' },1246        { builder: p3Builder, label: 'p3' },1247        { builder: p4Builder, label: 'p4' },1248      ]);1249      // Assert1250      expect(p1Builder).not.toHaveBeenCalled();1251      expect(p2Builder).not.toHaveBeenCalled();1252      expect(p3Builder).not.toHaveBeenCalled();1253      expect(p4Builder).not.toHaveBeenCalled();1254      await s.waitOne();1255      expect(p1Builder).toHaveBeenCalled();1256      expect(p2Builder).not.toHaveBeenCalled();1257      expect(p3Builder).not.toHaveBeenCalled();1258      expect(p4Builder).not.toHaveBeenCalled();1259      await s.waitOne();1260      expect(p1Builder).toHaveBeenCalled();1261      expect(p2Builder).toHaveBeenCalled();1262      expect(p3Builder).not.toHaveBeenCalled();1263      expect(p4Builder).not.toHaveBeenCalled();1264      await s.waitOne();1265      expect(p1Builder).toHaveBeenCalled();1266      expect(p2Builder).toHaveBeenCalled();1267      expect(p3Builder).toHaveBeenCalled();1268      expect(p4Builder).not.toHaveBeenCalled();1269      await s.waitOne();1270      expect(p1Builder).toHaveBeenCalled();1271      expect(p2Builder).toHaveBeenCalled();1272      expect(p3Builder).toHaveBeenCalled();1273      expect(p4Builder).toHaveBeenCalled();1274      expect(s.count()).toBe(0);1275    });1276    it('should wait the full completion of items coming from the scheduled sequence before taking any other scheduled promise', async () => {1277      // Arrange1278      const delay = () => new Promise((resolve) => setTimeout(resolve, 0));1279      const p1BuilderSteps = { a: false, b: false, c: false, d: false };1280      const p2Builder = jest.fn().mockResolvedValue(2);1281      const act = jest.fn().mockImplementation((f) => f());1282      const nextTaskIndex = jest.fn().mockReturnValue(0);1283      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1284      // Act1285      const s = new SchedulerImplem(act, taskSelector);1286      s.scheduleSequence([1287        {1288          builder: () => {1289            const complexSequenceItem = async () => {1290              p1BuilderSteps.a = true;1291              await delay();1292              p1BuilderSteps.b = true;1293              await delay();1294              p1BuilderSteps.c = true;1295              await delay();1296              p1BuilderSteps.d = true;1297            };1298            return complexSequenceItem();1299          },1300          label: 'p1',1301        },1302        { builder: p2Builder, label: 'p2' },1303      ]);1304      // Assert1305      expect(p1BuilderSteps).toEqual({ a: false, b: false, c: false, d: false });1306      expect(p2Builder).not.toHaveBeenCalled();1307      await s.waitOne();1308      expect(p1BuilderSteps).toEqual({ a: true, b: true, c: true, d: true });1309      expect(p2Builder).not.toHaveBeenCalled();1310      await s.waitAll();1311    });1312    it('should show item name declared in sequence in toString', async () => {1313      // Arrange1314      const act = jest.fn().mockImplementation((f) => f());1315      const nextTaskIndex = jest.fn().mockReturnValue(0);1316      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1317      // Act1318      const s = new SchedulerImplem(act, taskSelector);1319      s.scheduleSequence([1320        { builder: () => Promise.resolve(42), label: 'firstStep' },1321        function anotherStep() {1322          return Promise.resolve(48);1323        },1324        { builder: () => Promise.reject(1), label: 'rejectedStep' },1325        { builder: () => Promise.resolve(8), label: 'neverCalled' },1326      ]);1327      // Assert1328      await s.waitAll();1329      expect(s.toString()).toMatchInlineSnapshot(`1330          "schedulerFor()\`1331          -> [task\${1}] sequence::firstStep resolved with value 421332          -> [task\${2}] sequence::anotherStep resolved with value 481333          -> [task\${3}] sequence::rejectedStep rejected with value 1\`"1334        `);1335      expect(s.count()).toBe(0);1336    });1337    it('should issue a task that resolves when the sequence ends successfully', async () => {1338      // Arrange1339      const act = jest.fn().mockImplementation((f) => f());1340      const nextTaskIndex = jest.fn().mockReturnValue(0);1341      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1342      // Act1343      let taskResolvedValue: { done: boolean; faulty: boolean } | null = null;1344      const s = new SchedulerImplem(act, taskSelector);1345      const { task } = s.scheduleSequence([1346        { builder: () => Promise.resolve(42), label: 'firstStep' },1347        { builder: () => Promise.resolve(8), label: 'secondStep' },1348      ]);1349      task.then((v) => (taskResolvedValue = v));1350      // Assert1351      while (s.count() !== 0) {1352        expect(taskResolvedValue).toBe(null);1353        await s.waitOne();1354      }1355      expect(taskResolvedValue).toEqual({ done: true, faulty: false });1356    });1357    it('should issue a task that resolves when the sequence fails', async () => {1358      // Arrange1359      const act = jest.fn().mockImplementation((f) => f());1360      const nextTaskIndex = jest.fn().mockReturnValue(0);1361      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1362      // Act1363      let taskResolvedValue: { done: boolean; faulty: boolean } | null = null;1364      const s = new SchedulerImplem(act, taskSelector);1365      const { task } = s.scheduleSequence([1366        { builder: () => Promise.resolve(42), label: 'firstStep' },1367        { builder: () => Promise.reject(8), label: 'secondStep' },1368        { builder: () => Promise.resolve(8), label: 'neverCalledStep' },1369      ]);1370      task.then((v) => (taskResolvedValue = v));1371      // Assert1372      while (s.count() !== 0) {1373        expect(taskResolvedValue).toBe(null);1374        await s.waitOne();1375      }1376      expect(taskResolvedValue).toEqual({ done: false, faulty: true });1377    });1378    it('should attach passed metadata into the report', async () => {1379      // Arrange1380      const expectedMetadataFirst = Symbol('123');1381      const expectedMetadataSecond = Symbol('1234');1382      const act = jest.fn().mockImplementation((f) => f());1383      const nextTaskIndex = jest.fn().mockReturnValue(0);1384      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1385      // Act1386      const s = new SchedulerImplem(act, taskSelector);1387      s.scheduleSequence([1388        { builder: () => Promise.resolve(42), label: 'firstStep', metadata: expectedMetadataFirst },1389        { builder: () => Promise.reject(8), label: 'secondStep', metadata: expectedMetadataSecond },1390      ]);1391      // Assert1392      await s.waitAll();1393      const report = s.report();1394      expect(report).toHaveLength(2);1395      expect(report[0].status).toBe('resolved');1396      expect(report[0].metadata).toBe(expectedMetadataFirst);1397      expect(report[1].status).toBe('rejected');1398      expect(report[1].metadata).toBe(expectedMetadataSecond);1399    });1400    it('should attach passed metadata into the report even if not executed', async () => {1401      // Arrange1402      const expectedMetadataFirst = Symbol('123');1403      const expectedMetadataSecond = Symbol('1234');1404      const act = jest.fn().mockImplementation((f) => f());1405      const nextTaskIndex = jest.fn().mockReturnValue(0);1406      const taskSelector: TaskSelector<unknown> = { clone: jest.fn(), nextTaskIndex };1407      // Act1408      const s = new SchedulerImplem(act, taskSelector);1409      s.scheduleSequence([1410        { builder: () => Promise.resolve(42), label: 'firstStep', metadata: expectedMetadataFirst },1411        { builder: () => Promise.reject(8), label: 'secondStep', metadata: expectedMetadataSecond },1412      ]);1413      // Assert1414      const report = s.report();1415      expect(report).toHaveLength(1); // second task cannot be scheduled as first one is still pending1416      expect(report[0].status).toBe('pending');1417      expect(report[0].metadata).toBe(expectedMetadataFirst);1418    });1419  });1420});1421// Helpers1422function buildSeededNextTaskIndex(seeds: fc.Stream<number>, nextTaskIndexLengths: number[] = []) {1423  const nextTaskIndex = jest.fn().mockImplementation((scheduledTasks: ScheduledTask<unknown>[]) => {1424    const seed = seeds.next();1425    if (seed.done) {1426      throw new Error('Stream for seeds exhausted');1427    }1428    if (scheduledTasks.length === 0) {1429      throw new Error('Called without any task');1430    }1431    // `tasks` pointer being re-used from one call to another (mutate)1432    // we cannot rely on toHaveBeenCalledWith1433    nextTaskIndexLengths.push(scheduledTasks.length);1434    return seed.value % scheduledTasks.length;1435  });1436  return nextTaskIndex;...Using AI Code Generation
1const then1Function = require('fast-check-monorepo').then1Function;2const then2Function = require('fast-check-monorepo').then2Function;3const test = () => {4}5test();6const { then1Function } = require('fast-check-monorepo');7const { then2Function } = require('fast-check-monorepo');8const test = () => {9}10test();11import { then1Function } from 'fast-check-monorepo';12import { then2Function } from 'fast-check-monorepo';13const test = () => {14}15test();16import { then1Function } from 'fast-check-monorepo/then1Function';17import { then2Function } from 'fast-check-monorepo/then2Function';18const test = () => {19}20test();21import then1Function from 'fast-check-monorepo/then1Function';22import then2Function from 'fast-check-monorepo/then2Function';23const test = () => {24}25test();26import then1Function from 'fast-check-monorepo/then1Function';27import then2Function from 'fast-check-monorepo/then2Function';28const test = () => {29}30test();31import then1Function from 'Using AI Code Generation
1const { then1Function } = require('fast-check-monorepo');2const { then2Function } = require('fast-check-monorepo');3const { then3Function } = require('fast-check-monorepo');4const { then4Function } = require('fast-check-monorepo');5const { then5Function } = require('fast-check-monorepo');6const { then6Function } = require('fast-check-monorepo');7const { then7Function } = require('fast-check-monorepo');8const { then8Function } = require('fast-check-monorepo');9const { then9Function } = require('fast-check-monorepo');10const { then10Function } = require('fast-check-monorepo');11const { then11Function } = require('fast-check-monorepo');12const { then12Function } = require('fast-check-monorepo');13const { then13Function } = require('fast-check-monorepo');14const { then14Function } = require('fast-check-monorepo');15const { then15Function } = require('fast-check-monorepo');16const { then16Function } = require('fast-check-monorepo');17const { then17Function } = require('fast-check-monorepo');Using AI Code Generation
1import {then1Function} from 'fast-check-monorepo';2console.log(then1Function());3import {then2Function} from 'fast-check-monorepo';4console.log(then2Function());5import {then3Function} from 'fast-check-monorepo';6console.log(then3Function());7import {then4Function} from 'fast-check-monorepo';8console.log(then4Function());9import {then5Function} from 'fast-check-monorepo';10console.log(then5Function());11import {then6Function} from 'fast-check-monorepo';12console.log(then6Function());13import {then7Function} from 'fast-check-monorepo';14console.log(then7Function());15import {then8Function} from 'fast-check-monorepo';16console.log(then8Function());17import {then9Function} from 'fast-check-monorepo';18console.log(then9Function());19import {then10Function} from 'fast-check-monorepo';20console.log(then10Function());21import {then11Function} from 'fast-check-monorepo';22console.log(then11Function());23import {then12Function} from 'fast-check-monorepo';24console.log(then12Function());Using AI Code Generation
1import { then1Function } from 'fast-check-monorepo';2const then1Function = require('fast-check-monorepo');3import { then2Function } from 'fast-check-monorepo';4const then2Function = require('fast-check-monorepo');5import { then3Function } from 'fast-check-monorepo';6const then3Function = require('fast-check-monorepo');7import { then4Function } from 'fast-check-monorepo';8const then4Function = require('fast-check-monorepo');9import { then5Function } from 'fast-check-monorepo';10const then5Function = require('fast-check-monorepo');11import { then6Function } from 'fast-check-monorepo';12const then6Function = require('fast-check-monorepo');13import { then7Function } from 'fast-check-monorepo';14const then7Function = require('fast-check-monorepo');15import { then8Function } from 'fast-check-monorepo';16const then8Function = require('fast-check-monorepo');17import { then9Function } from 'fast-check-monorepo';18const then9Function = require('fast-check-monorepo');19import { then10Function } from 'fast-check-monorepo';20const then10Function = require('fast-check-monorepo');21import { then11Function } from 'fast-check-monorepo';22const then11Function = require('fast-check-monorepo');23import { then12Function } from 'fast-check-monorepo';24const then12Function = require('fast-check-monorepo');Using AI Code Generation
1import {then1Function} from 'fast-check-monorepo';2import {then1Function} from 'fast-check-monorepo';3import {then1Function} from 'fast-check-monorepo';4import {then1Function} from 'fast-check-monorepo';5import {then1Function} from 'fast-check-monorepo';6import {then1Function} from 'fast-check-monorepo';7import {then1Function} from 'fast-check-monorepo';8import {then1Function} from 'fast-check-monorepo';9import {then1Function} from 'fast-check-monorepo';10import {then1Function} from 'fast-check-monorepo';11import {then1Function} from 'fast-check-monorepo';12import {then1Function} from 'fast-check-monorepo';13import {then1Function} from 'fast-check-monorepo';14import {then1Function} from 'fast-check-monorepo';15import {then1Function} from 'fast-check-monorepo';16import {then1Function} from 'fast-check-monorepo';Using AI Code Generation
1const then1Function = require('fast-check-monorepo').then1Function2const then1Function = require('fast-check-monorepo/then1Function').then1Function3const then1Function = require('fast-check-monorepo/then1Function')4const then1Function = require('fast-check-monorepo').then1Function5const then1Function = require('fast-check-monorepo/then1Function').then1Function6const then1Function = require('fast-check-monorepo/then1Function')7const then1Function = require('fast-check-monorepo').then1Function8const then1Function = require('fast-check-monorepo/then1Function').then1Function9const then1Function = require('fast-check-monorepo/then1Function')10const then1Function = require('fast-check-monorepo').then1Function11const then1Function = require('fast-check-monorepo/then1Function').then1Function12const then1Function = require('fast-check-monorepo/then1Function')13const then1Function = require('fastUsing AI Code Generation
1const {then1Function} = require('fast-check-monorepo');2const result = then1Function(2,3);3console.log(result);4const {then1Function} = require('fast-check-monorepo');5const result = then1Function(2,3);6console.log(result);7const {then1Function} = require('fast-check-monorepo');8const result = then1Function(2,3);9console.log(result);10const {then1Function} = require('fast-check-monorepo');11const result = then1Function(2,3);12console.log(result);13const {then1Function} = require('fast-check-monorepo');14const result = then1Function(2,3);15console.log(result);16const {then1Function} = require('fast-check-monorepo');17const result = then1Function(2,3);18console.log(result);19const {then1Function} = require('fast-check-monorepo');20const result = then1Function(2,3);21console.log(result);22const {then1Function} = require('fast-check-monorepo');23const result = then1Function(2,3);24console.log(result);25const {then1Function} = require('fast-check-monorepo');26const result = then1Function(2,3);27console.log(result);28const {then1Function} = require('fast-check-monorepo');29const result = then1Function(2,3);30console.log(result);Using AI Code Generation
1const then1Function = require('fast-check-monorepo').then1Function2import { then1Function } from 'fast-check-monorepo'3const then1Function = require('fast-check-monorepo').then1Function4import { then1Function } from 'fast-check-monorepo'5const then1Function = require('fast-check-monorepo').then1Function6import { then1Function } from 'fast-check-monorepo'7const then1Function = require('fast-check-monorepo').then1Function8import { then1Function } from 'fast-check-monorepo'9const then1Function = require('fast-check-monorepo').then1Function10import { then1Function } from 'fast-check-monorepo'Using AI Code Generation
1const then1Function = require('fast-check-monorepo');2const fc = require('fast-check');3const then1 = then1Function({4  num: fc.integer(),5  str: fc.string(),6});7const myTest = () => {8  return then1(9    (num, str) => {10      console.log(`num: ${num}, str: ${str}`);11    },12    (num, str) => {13      console.log(`num: ${num}, str: ${str}`);14    },15  );16};17myTest();18{19  "dependencies": {20  },21  "devDependencies": {},22  "scripts": {23  },24}25{26  "dependencies": {27  },28  "devDependencies": {},29  "scripts": {30  },31}32const fc = require('fast-check');33const then1Function = (generators) => {34  return (then1, then2) => {35    return fc.assert(36      fc.property(fc.tuple(...Object.values(generators)), (args) => {37        then1(...args);38        then2(...args);39      }),40    );41  };42};43module.exports = then1Function;Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
