UNPKG

atlas-relax

Version:

A minimal, powerful declarative VDOM and reactive programming framework.

269 lines (266 loc) 9.46 kB
const { describe, it } = require("mocha") const { expect } = require("chai") const { Frame, diff } = require("../"); // * closures are nice, you can specify your rendered callback on every render // and it will automatically refer to the correct template. // * since we would like to support rendered as a class method, we need to // make sure we also supply the correct template as an argument to rendered. // * opinionated patterns may be implemented on top of these methods. // e.g. useEffect(...) describe("lifecycle methods/hooks", function(){ describe("rendered", function(){ it("should cancel the hook if unset before flush", function(){ let calledRendered = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.rendered = () => calledRendered++; upd(node); } else if (calledRender === 2) { node.rendered = null; } } const f = upd() expect(calledRender).to.equal(2); expect(calledRendered).to.equal(0); upd(f); expect(calledRender).to.equal(3); expect(calledRendered).to.equal(0) }) it("should cancel the hook if unset before flush even if there's a cleanup hook", function(){ let calledRendered = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.rendered = () => calledRendered++; node.cleanup = () => {}; upd(node); } else if (calledRender === 2) { node.rendered = null; } } const f = upd() expect(calledRender).to.equal(2); expect(calledRendered).to.equal(0); upd(f); expect(calledRender).to.equal(3); expect(calledRendered).to.equal(0) }) it("should cancel the hook after running it if unset after flush", function(){ let calledRendered = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.rendered = () => { calledRendered++; node.rendered = null; } upd(node); } } const f = upd() expect(calledRender).to.equal(2); expect(calledRendered).to.equal(1); upd(f); expect(calledRender).to.equal(3); expect(calledRendered).to.equal(1) }) it("should cancel the hook after running it if unset after flush even if there's a cleanup hook", function(){ let calledRendered = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.cleanup = () => {}; node.rendered = () => { calledRendered++; node.rendered = null; } upd(node); } } const f = upd() expect(calledRender).to.equal(2); expect(calledRendered).to.equal(1); upd(f); expect(calledRender).to.equal(3); expect(calledRendered).to.equal(1) }) }) describe("cleanup", function(){ it("should run when nodes are unmounted", function(){ let calledIds = []; const h = (id, next) => ({name: Node, data: {id}, next}) const Node = (t, f) => { f.cleanup = () => { calledIds.push(t.data.id); } return t.next; } const f = diff(h(0, [h(1), h(2, [h(3), h(4)])])) diff(null, f); expect(calledIds).to.eql([0,2,4,3,1]); }) it("should run with the latest template closure", function(){ let calledCleanup = 0, calledRender = 0; const N = 10; const Node = (temp, node) => { calledRender++; node.cleanup = () => { calledCleanup++; expect(temp.data.id).to.equal(N) } const { name, data: { id } } = temp; if (id < N){ diff({name, data: {id: id+1}}, node); } else { diff(null, node); } } diff({name: Node, data: {id: 0}}); expect(calledRender).to.equal(N+1); expect(calledCleanup).to.equal(1); }) it("should run with the last template closure used in render if outer-diffed post-flush", function(){ let calledCleanup = 0, calledRendered = 0, r; const N = 10; const h = (id, run, next) => ({name: Node, data: {id, run}, next}) const Node = (temp, f) => { if (temp.data.id === 4){ f.rendered = f.rendered || (() => { calledRendered++; diff(h(0, 0, [h(1), 0, h(2, 0, [h(3), h(4)])]), r); diff(null, r) }) } else if (!temp.data.id) { f.cleanup = () => { calledCleanup++ expect(temp.data.run).to.equal(N) }; r = f; if (temp.data.run < N){ diff(h(0, temp.data.run + 1, [h(1), 0, h(2, 0, [h(3), h(4)])]), r) } } return temp.next; } diff(h(0, 0, [h(1), 0, h(2, 0, [h(3), h(4)])])) expect(calledRendered).to.equal(1); expect(calledCleanup).to.equal(1); }) it("should cancel any pending rendered hooks if unmounted before flush", function(){ let calledRendered = 0, r; const h = (id, next) => ({name: Node, data: {id}, next}) const Node = (t, f) => { f.cleanup = () => {}; f.rendered = () => calledRendered++; if (t.data.id === 4) diff(null, r) else if (!t.data.id) r = f; return t.next; } diff(h(0, [h(1), h(2, [h(3), h(4)])])) expect(calledRendered).to.equal(0); }) it("should cancel any pending rendered hooks if unmounted after flush", function(){ let calledRendered = 0, r; const h = (id, next) => ({name: Node, data: {id}, next}) const Node = (temp, f) => { f.cleanup = () => {}; f.rendered = t => { calledRendered++; if (temp.data.id === 4) diff(null, r) } if (!temp.data.id) r = f; return temp.next; } diff(h(0, [h(1), h(2, [h(3), h(4)])])) expect(calledRendered).to.equal(1); }) it("should cancel the hook if unset before flush", function(){ let calledCleanup = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.cleanup = () => calledCleanup++; upd(node); } else if (calledRender === 2) { node.cleanup = null; } } const f = upd() expect(calledRender).to.equal(2); expect(calledCleanup).to.equal(0); upd(f); expect(calledRender).to.equal(3); expect(calledCleanup).to.equal(0); diff(null, f); expect(calledRender).to.equal(3); expect(calledCleanup).to.equal(0) }) it("should cancel the hook if unset before flush even if there's a rendered hook", function(){ let calledCleanup = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.cleanup = () => calledCleanup++; node.rendered = () => {}; upd(node); } else if (calledRender === 2) { node.cleanup = null; } } const f = upd() expect(calledRender).to.equal(2); expect(calledCleanup).to.equal(0); upd(f); expect(calledRender).to.equal(3); expect(calledCleanup).to.equal(0); diff(null, f); expect(calledRender).to.equal(3); expect(calledCleanup).to.equal(0) }) it("should cancel the hook if unset after flush", function(){ let calledRendered = 0, calledCleanup = 0, calledRender = 0; const upd = f => diff({name: Node, data: {id: calledRender}}, f); const Node = (temp, node) => { expect(temp.data.id).to.equal(calledRender) if (!calledRender++){ node.cleanup = () => calledCleanup++; node.rendered = () => { calledRendered++; node.cleanup = null; } upd(node); } } const f = upd() expect(calledRender).to.equal(2); expect(calledRendered).to.equal(1); expect(calledCleanup).to.equal(0) diff(null, f); expect(calledRender).to.equal(2); expect(calledRendered).to.equal(1); expect(calledCleanup).to.equal(0) }) it("should call the hook even if node gets unmounted before it mounts", function(){ let calledIds = [], r; const h = (id, next) => ({name: Node, data: {id}, next}) const Node = (t, f) => { f.cleanup = () => { calledIds.push(t.data.id); } if (t.data.id === 4) diff(null, r); else if (!t.data.id) r = f; return t.next; } diff(h(0, [h(1), h(2, [h(3), h(4)])])) diff(null, r); expect(calledIds).to.eql([0,2,4,3,1]); }) }) })