thunk.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import { assert } from "chai";
  2. import { init, h, thunk, VNode } from "../../src/index";
  3. const patch = init([]);
  4. describe("thunk", function () {
  5. let elm: any, vnode0: any;
  6. beforeEach(function () {
  7. elm = vnode0 = document.createElement("div");
  8. });
  9. it("returns vnode with data and render function", function () {
  10. function numberInSpan(n: number) {
  11. return h("span", `Number is ${n}`);
  12. }
  13. const vnode = thunk("span", "num", numberInSpan, [22]);
  14. assert.deepEqual(vnode.sel, "span");
  15. assert.deepEqual(vnode.data.key, "num");
  16. assert.deepEqual(vnode.data.args, [22]);
  17. });
  18. it("calls render function once on data change", function () {
  19. let called = 0;
  20. function numberInSpan(n: number) {
  21. called++;
  22. return h("span", { key: "num" }, `Number is ${n}`);
  23. }
  24. const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  25. const vnode2 = h("div", [thunk("span", "num", numberInSpan, [2])]);
  26. patch(vnode0, vnode1);
  27. assert.strictEqual(called, 1);
  28. patch(vnode1, vnode2);
  29. assert.strictEqual(called, 2);
  30. });
  31. it("does not call render function on data unchanged", function () {
  32. let called = 0;
  33. function numberInSpan(n: number) {
  34. called++;
  35. return h("span", { key: "num" }, `Number is ${n}`);
  36. }
  37. const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  38. const vnode2 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  39. patch(vnode0, vnode1);
  40. assert.strictEqual(called, 1);
  41. patch(vnode1, vnode2);
  42. assert.strictEqual(called, 1);
  43. });
  44. it("calls render function once on data-length change", function () {
  45. let called = 0;
  46. function numberInSpan(n: number) {
  47. called++;
  48. return h("span", { key: "num" }, `Number is ${n}`);
  49. }
  50. const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  51. const vnode2 = h("div", [thunk("span", "num", numberInSpan, [1, 2])]);
  52. patch(vnode0, vnode1);
  53. assert.strictEqual(called, 1);
  54. patch(vnode1, vnode2);
  55. assert.strictEqual(called, 2);
  56. });
  57. it("calls render function once on function change", function () {
  58. let called = 0;
  59. function numberInSpan(n: number) {
  60. called++;
  61. return h("span", { key: "num" }, `Number is ${n}`);
  62. }
  63. function numberInSpan2(n: number) {
  64. called++;
  65. return h("span", { key: "num" }, `Number really is ${n}`);
  66. }
  67. const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  68. const vnode2 = h("div", [thunk("span", "num", numberInSpan2, [1])]);
  69. patch(vnode0, vnode1);
  70. assert.strictEqual(called, 1);
  71. patch(vnode1, vnode2);
  72. assert.strictEqual(called, 2);
  73. });
  74. it("renders correctly", function () {
  75. let called = 0;
  76. function numberInSpan(n: number) {
  77. called++;
  78. return h("span", { key: "num" }, `Number is ${n}`);
  79. }
  80. const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  81. const vnode2 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  82. const vnode3 = h("div", [thunk("span", "num", numberInSpan, [2])]);
  83. elm = patch(vnode0, vnode1).elm;
  84. assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span");
  85. assert.strictEqual(elm.firstChild.innerHTML, "Number is 1");
  86. elm = patch(vnode1, vnode2).elm;
  87. assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span");
  88. assert.strictEqual(elm.firstChild.innerHTML, "Number is 1");
  89. elm = patch(vnode2, vnode3).elm;
  90. assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span");
  91. assert.strictEqual(elm.firstChild.innerHTML, "Number is 2");
  92. assert.strictEqual(called, 2);
  93. });
  94. it("supports leaving out the `key` argument", function () {
  95. function vnodeFn(s: string) {
  96. return h("span.number", "Hello " + s);
  97. }
  98. const vnode1 = thunk("span.number", vnodeFn, ["World!"]);
  99. elm = patch(vnode0, vnode1).elm;
  100. assert.strictEqual(elm.innerText, "Hello World!");
  101. });
  102. it("renders correctly when root", function () {
  103. let called = 0;
  104. function numberInSpan(n: number) {
  105. called++;
  106. return h("span", { key: "num" }, `Number is ${n}`);
  107. }
  108. const vnode1 = thunk("span", "num", numberInSpan, [1]);
  109. const vnode2 = thunk("span", "num", numberInSpan, [1]);
  110. const vnode3 = thunk("span", "num", numberInSpan, [2]);
  111. elm = patch(vnode0, vnode1).elm;
  112. assert.strictEqual(elm.tagName.toLowerCase(), "span");
  113. assert.strictEqual(elm.innerHTML, "Number is 1");
  114. elm = patch(vnode1, vnode2).elm;
  115. assert.strictEqual(elm.tagName.toLowerCase(), "span");
  116. assert.strictEqual(elm.innerHTML, "Number is 1");
  117. elm = patch(vnode2, vnode3).elm;
  118. assert.strictEqual(elm.tagName.toLowerCase(), "span");
  119. assert.strictEqual(elm.innerHTML, "Number is 2");
  120. assert.strictEqual(called, 2);
  121. });
  122. it("can be replaced and removed", function () {
  123. function numberInSpan(n: number) {
  124. return h("span", { key: "num" }, `Number is ${n}`);
  125. }
  126. function oddEven(n: number): VNode {
  127. const prefix = n % 2 === 0 ? "Even" : "Odd";
  128. return h("div", { key: oddEven as any }, `${prefix}: ${n}`);
  129. }
  130. const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]);
  131. const vnode2 = h("div", [thunk("div", "oddEven", oddEven, [4])]);
  132. elm = patch(vnode0, vnode1).elm;
  133. assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span");
  134. assert.strictEqual(elm.firstChild.innerHTML, "Number is 1");
  135. elm = patch(vnode1, vnode2).elm;
  136. assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "div");
  137. assert.strictEqual(elm.firstChild.innerHTML, "Even: 4");
  138. });
  139. it("can be replaced and removed when root", function () {
  140. function numberInSpan(n: number) {
  141. return h("span", { key: "num" }, `Number is ${n}`);
  142. }
  143. function oddEven(n: number): VNode {
  144. const prefix = n % 2 === 0 ? "Even" : "Odd";
  145. return h("div", { key: oddEven as any }, `${prefix}: ${n}`);
  146. }
  147. const vnode1 = thunk("span", "num", numberInSpan, [1]);
  148. const vnode2 = thunk("div", "oddEven", oddEven, [4]);
  149. elm = patch(vnode0, vnode1).elm;
  150. assert.strictEqual(elm.tagName.toLowerCase(), "span");
  151. assert.strictEqual(elm.innerHTML, "Number is 1");
  152. elm = patch(vnode1, vnode2).elm;
  153. assert.strictEqual(elm.tagName.toLowerCase(), "div");
  154. assert.strictEqual(elm.innerHTML, "Even: 4");
  155. });
  156. it("invokes destroy hook on thunks", function () {
  157. let called = 0;
  158. function destroyHook() {
  159. called++;
  160. }
  161. function numberInSpan(n: number) {
  162. return h(
  163. "span",
  164. { key: "num", hook: { destroy: destroyHook } },
  165. `Number is ${n}`
  166. );
  167. }
  168. const vnode1 = h("div", [
  169. h("div", "Foo"),
  170. thunk("span", "num", numberInSpan, [1]),
  171. h("div", "Foo"),
  172. ]);
  173. const vnode2 = h("div", [h("div", "Foo"), h("div", "Foo")]);
  174. patch(vnode0, vnode1);
  175. patch(vnode1, vnode2);
  176. assert.strictEqual(called, 1);
  177. });
  178. it("invokes remove hook on thunks", function () {
  179. let called = 0;
  180. function hook() {
  181. called++;
  182. }
  183. function numberInSpan(n: number) {
  184. return h(
  185. "span",
  186. { key: "num", hook: { remove: hook } },
  187. `Number is ${n}`
  188. );
  189. }
  190. const vnode1 = h("div", [
  191. h("div", "Foo"),
  192. thunk("span", "num", numberInSpan, [1]),
  193. h("div", "Foo"),
  194. ]);
  195. const vnode2 = h("div", [h("div", "Foo"), h("div", "Foo")]);
  196. patch(vnode0, vnode1);
  197. patch(vnode1, vnode2);
  198. assert.strictEqual(called, 1);
  199. });
  200. });