jevstrudel.git / website / src / jev / seek.test.mjs
1import { describe, expect, it } from 'vitest';
2import { decideThrough } from './jevCore.mjs';
3import { Cyclist } from '../../../packages/core/cyclist.mjs';
4
5// a jev() as decideThrough sees it: `decide(segment)` resolves to its
6// decision, or null past where the song ends; `last` once the ending is known
7const view = (endsAt, log) => {
8  const v = { last: null };
9  v.decide = async (s) => {
10    log.push(s);
11    if (endsAt !== null && s > endsAt) return null;
12    if (s === endsAt) v.last = s;
13    return { segment: s };
14  };
15  return v;
16};
17
18describe('decideThrough', () => {
19  it('asks each section in order, every jev() of one before any of the next', async () => {
20    const log = [];
21    const reached = await decideThrough([view(null, log), view(null, log)], 3);
22    expect(reached).toBe(3);
23    expect(log).toEqual([0, 0, 1, 1, 2, 2, 3, 3]);
24  });
25  it('stops on the section the song ends on, and asks nothing past it', async () => {
26    const log = [];
27    expect(await decideThrough([view(2, log)], 6)).toBe(2);
28    expect(log).toEqual([0, 1, 2]);
29  });
30});
31
32describe('Cyclist.seek', () => {
33  it('begins the next tick at the cycle sought, back or forward', () => {
34    const queried = [];
35    let now = 0;
36    const c = new Cyclist({ getTime: () => now, setInterval: () => 0, clearInterval: () => {} });
37    c.pattern = { queryArc: (b, e) => (queried.push([b, e]), []) };
38    c.setCps(1);
39    c.clock.start();
40    const first = queried.length;
41    c.seek(40);
42    now = 0.1;
43    c.clock.start();
44    expect(queried[first][0]).toBe(40);
45    expect(queried[first][1]).toBeGreaterThan(40);
46    c.seek(8);
47    now = 0.2;
48    c.clock.start();
49    expect(queried.at(-1)[0]).toBeGreaterThanOrEqual(8);
50    expect(queried.at(-1)[1]).toBeLessThan(9);
51  });
52});