import { describe, expect, test } from 'vitest'; import { Cmd, Dispatcher, Maybe, noCmd, nothing, Result, Sub, Task } from 'tea-cup-fp'; import { render } from '@testing-library/react'; import * as React from 'react'; import { needsFlush, Program } from './Program'; interface Model { readonly id: Maybe; readonly other: boolean; } type Msg = { tag: 'got-load'; id: Result } | { tag: 'got-other' }; function init(): [Model, Cmd] { const cmd: Cmd = Task.attempt( Task.fromPromise( () => new Promise((res) => setTimeout(() => res('myid'), 1000)), ), (r) => ({ tag: 'got-load', id: r }), ); return [{ id: nothing, other: false }, cmd]; } function view(model: Model) { const id = model.id.withDefault('tmpid'); return (
{id},{model.other ? 'true' : 'false'}
); } function update(msg: Msg, model: Model): [Model, Cmd] { switch (msg.tag) { case 'got-load': { const newModel: Model = { ...model, id: msg.id.toMaybe(), }; const t: Maybe> = msg.id.toMaybe().map((id) => Task.fromLambda(() => { const n = document.getElementById(id); if (!n) { throw new Error('node not found'); } return n; }), ); const t2: Maybe> = t.map((task) => task.map((e) => { return true; }), ); const msgOther: Msg = { tag: 'got-other' }; const cmd: Cmd = t2.map((task) => Task.attempt(task, () => msgOther)).withDefaultSupply(() => Cmd.none()); return [newModel, cmd]; } case 'got-other': { const newModel: Model = { ...model, other: true, }; return noCmd(newModel); } } } function subscriptions(): Sub { return Sub.none(); } describe('program test', () => { test('view should be called after each update', () => new Promise((done) => { let initCount = 0; let viewCount = 0; let updateCount = 0; let subsCount = 0; const myInit = () => { initCount++; return init(); }; const myView = (_d: Dispatcher, m: Model) => { viewCount++; return view(m); }; const myUpdate = (msg: Msg, model: Model) => { updateCount++; return update(msg, model); }; const mySubs = (_model: Model) => { subsCount++; return subscriptions(); }; const p = ; const { container } = render(p); expect(container.querySelector('#tmpid')?.textContent).toEqual('tmpid,false'); expect(initCount).toBe(1); expect(viewCount).toBe(1); expect(updateCount).toBe(0); expect(subsCount).toBe(1); setTimeout(() => { expect(container.querySelector('#myid')?.textContent).toEqual('myid,true'); expect(initCount).toBe(1); expect(viewCount).toBe(3); expect(updateCount).toBe(2); expect(subsCount).toBe(3); done(); }, 3000); })); test('needs to flush default', () => { expect(needsFlush(undefined, undefined)).toBe(true); expect(needsFlush(undefined, true)).toBe(true); expect(needsFlush(undefined, false)).toBe(false); expect(needsFlush(true, undefined)).toBe(true); expect(needsFlush(true, true)).toBe(true); expect(needsFlush(true, false)).toBe(false); expect(needsFlush(false, undefined)).toBe(false); expect(needsFlush(false, true)).toBe(true); expect(needsFlush(false, false)).toBe(false); }); });