@gogocat/data-bind 1.11.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/.editorconfig +14 -14
  2. package/.vscode/launch.json +12 -12
  3. package/CONFIGURATION.md +294 -0
  4. package/REACTIVE_MODE.md +553 -0
  5. package/README.md +266 -829
  6. package/babel.config.json +30 -0
  7. package/dist/js/_escape.d.ts +14 -0
  8. package/dist/js/_escape.d.ts.map +1 -0
  9. package/dist/js/applyBinding.d.ts +11 -0
  10. package/dist/js/applyBinding.d.ts.map +1 -0
  11. package/dist/js/attrBinding.d.ts +12 -0
  12. package/dist/js/attrBinding.d.ts.map +1 -0
  13. package/dist/js/binder.d.ts +67 -0
  14. package/dist/js/binder.d.ts.map +1 -0
  15. package/dist/js/changeBinding.d.ts +19 -0
  16. package/dist/js/changeBinding.d.ts.map +1 -0
  17. package/dist/js/commentWrapper.d.ts +39 -0
  18. package/dist/js/commentWrapper.d.ts.map +1 -0
  19. package/dist/js/config.d.ts +55 -0
  20. package/dist/js/config.d.ts.map +1 -0
  21. package/dist/js/createBindingOption.d.ts +32 -0
  22. package/dist/js/createBindingOption.d.ts.map +1 -0
  23. package/dist/js/createEventBinding.d.ts +10 -0
  24. package/dist/js/createEventBinding.d.ts.map +1 -0
  25. package/dist/js/cssBinding.d.ts +15 -0
  26. package/dist/js/cssBinding.d.ts.map +1 -0
  27. package/dist/js/dataBind.js +2772 -2519
  28. package/dist/js/dataBind.min.js +8 -1
  29. package/dist/js/dataBind.min.js.map +1 -1
  30. package/dist/js/domWalker.d.ts +9 -0
  31. package/dist/js/domWalker.d.ts.map +1 -0
  32. package/dist/js/forOfBinding.d.ts +12 -0
  33. package/dist/js/forOfBinding.d.ts.map +1 -0
  34. package/dist/js/hoverBinding.d.ts +13 -0
  35. package/dist/js/hoverBinding.d.ts.map +1 -0
  36. package/dist/js/ifBinding.d.ts +12 -0
  37. package/dist/js/ifBinding.d.ts.map +1 -0
  38. package/dist/js/index.d.ts +10 -0
  39. package/dist/js/index.d.ts.map +1 -0
  40. package/dist/js/modelBinding.d.ts +12 -0
  41. package/dist/js/modelBinding.d.ts.map +1 -0
  42. package/dist/js/postProcess.d.ts +3 -0
  43. package/dist/js/postProcess.d.ts.map +1 -0
  44. package/dist/js/pubSub.d.ts +11 -0
  45. package/dist/js/pubSub.d.ts.map +1 -0
  46. package/dist/js/reactiveProxy.d.ts +28 -0
  47. package/dist/js/reactiveProxy.d.ts.map +1 -0
  48. package/dist/js/renderForOfBinding.d.ts +8 -0
  49. package/dist/js/renderForOfBinding.d.ts.map +1 -0
  50. package/dist/js/renderIfBinding.d.ts +22 -0
  51. package/dist/js/renderIfBinding.d.ts.map +1 -0
  52. package/dist/js/renderIteration.d.ts +16 -0
  53. package/dist/js/renderIteration.d.ts.map +1 -0
  54. package/dist/js/renderTemplate.d.ts +14 -0
  55. package/dist/js/renderTemplate.d.ts.map +1 -0
  56. package/dist/js/renderTemplatesBinding.d.ts +19 -0
  57. package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
  58. package/dist/js/showBinding.d.ts +13 -0
  59. package/dist/js/showBinding.d.ts.map +1 -0
  60. package/dist/js/switchBinding.d.ts +13 -0
  61. package/dist/js/switchBinding.d.ts.map +1 -0
  62. package/dist/js/textBinding.d.ts +13 -0
  63. package/dist/js/textBinding.d.ts.map +1 -0
  64. package/dist/js/types/_escape.d.ts +14 -0
  65. package/dist/js/types/_escape.d.ts.map +1 -0
  66. package/dist/js/types/applyBinding.d.ts +11 -0
  67. package/dist/js/types/applyBinding.d.ts.map +1 -0
  68. package/dist/js/types/attrBinding.d.ts +12 -0
  69. package/dist/js/types/attrBinding.d.ts.map +1 -0
  70. package/dist/js/types/binder.d.ts +67 -0
  71. package/dist/js/types/binder.d.ts.map +1 -0
  72. package/dist/js/types/changeBinding.d.ts +19 -0
  73. package/dist/js/types/changeBinding.d.ts.map +1 -0
  74. package/dist/js/types/commentWrapper.d.ts +39 -0
  75. package/dist/js/types/commentWrapper.d.ts.map +1 -0
  76. package/dist/js/types/config.d.ts +55 -0
  77. package/dist/js/types/config.d.ts.map +1 -0
  78. package/dist/js/types/createBindingOption.d.ts +32 -0
  79. package/dist/js/types/createBindingOption.d.ts.map +1 -0
  80. package/dist/js/types/createEventBinding.d.ts +10 -0
  81. package/dist/js/types/createEventBinding.d.ts.map +1 -0
  82. package/dist/js/types/cssBinding.d.ts +15 -0
  83. package/dist/js/types/cssBinding.d.ts.map +1 -0
  84. package/dist/js/types/domWalker.d.ts +9 -0
  85. package/dist/js/types/domWalker.d.ts.map +1 -0
  86. package/dist/js/types/forOfBinding.d.ts +12 -0
  87. package/dist/js/types/forOfBinding.d.ts.map +1 -0
  88. package/dist/js/types/hoverBinding.d.ts +13 -0
  89. package/dist/js/types/hoverBinding.d.ts.map +1 -0
  90. package/dist/js/types/ifBinding.d.ts +12 -0
  91. package/dist/js/types/ifBinding.d.ts.map +1 -0
  92. package/dist/js/types/index.d.ts +10 -0
  93. package/dist/js/types/index.d.ts.map +1 -0
  94. package/dist/js/types/modelBinding.d.ts +12 -0
  95. package/dist/js/types/modelBinding.d.ts.map +1 -0
  96. package/dist/js/types/postProcess.d.ts +3 -0
  97. package/dist/js/types/postProcess.d.ts.map +1 -0
  98. package/dist/js/types/pubSub.d.ts +11 -0
  99. package/dist/js/types/pubSub.d.ts.map +1 -0
  100. package/dist/js/types/reactiveProxy.d.ts +28 -0
  101. package/dist/js/types/reactiveProxy.d.ts.map +1 -0
  102. package/dist/js/types/renderForOfBinding.d.ts +8 -0
  103. package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
  104. package/dist/js/types/renderIfBinding.d.ts +22 -0
  105. package/dist/js/types/renderIfBinding.d.ts.map +1 -0
  106. package/dist/js/types/renderIteration.d.ts +16 -0
  107. package/dist/js/types/renderIteration.d.ts.map +1 -0
  108. package/dist/js/types/renderTemplate.d.ts +14 -0
  109. package/dist/js/types/renderTemplate.d.ts.map +1 -0
  110. package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
  111. package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
  112. package/dist/js/types/showBinding.d.ts +13 -0
  113. package/dist/js/types/showBinding.d.ts.map +1 -0
  114. package/dist/js/types/switchBinding.d.ts +13 -0
  115. package/dist/js/types/switchBinding.d.ts.map +1 -0
  116. package/dist/js/types/textBinding.d.ts +13 -0
  117. package/dist/js/types/textBinding.d.ts.map +1 -0
  118. package/dist/js/types/types.d.ts +111 -0
  119. package/dist/js/types/types.d.ts.map +1 -0
  120. package/dist/js/types/util.d.ts +119 -0
  121. package/dist/js/types/util.d.ts.map +1 -0
  122. package/dist/js/types.d.ts +111 -0
  123. package/dist/js/types.d.ts.map +1 -0
  124. package/dist/js/util.d.ts +119 -0
  125. package/dist/js/util.d.ts.map +1 -0
  126. package/eslint.config.js +124 -0
  127. package/examples/DBMONSTER_COMPARISON.md +123 -0
  128. package/examples/afterRenderDemo.html +119 -0
  129. package/examples/bootstrap/css/animate.css +1579 -1579
  130. package/examples/bootstrap/css/bootstrap.min.css +6 -6
  131. package/examples/bootstrap/css/homeservices.css +378 -390
  132. package/examples/bootstrap/css/open-iconic.css +511 -511
  133. package/examples/bootstrap/fonts/open-iconic.svg +543 -543
  134. package/examples/bootstrap/js/compMessageDialog.js +20 -19
  135. package/examples/bootstrap/js/compSearchBar.js +12 -19
  136. package/examples/bootstrap/js/compSearchResults.js +50 -46
  137. package/examples/bootstrap/js/featureAdsResult.json +65 -65
  138. package/examples/bootstrap/js/searchResult.json +57 -57
  139. package/examples/bootstrap.html +343 -332
  140. package/examples/css/baseTodo.css +141 -141
  141. package/examples/css/dbMonsterStyles.css +27 -27
  142. package/examples/css/indexTodo.css +374 -374
  143. package/examples/dbmonsterForOfReactive.html +40 -0
  144. package/examples/dbmonsterReact.html +19 -0
  145. package/examples/forOfBindingSimpleDebug.html +45 -0
  146. package/examples/form.html +20 -4
  147. package/examples/globalConfig.html +131 -0
  148. package/examples/js/afterRenderDemo.js +190 -0
  149. package/examples/js/appTodo.js +46 -46
  150. package/examples/js/attrBindingDemo.js +2 -2
  151. package/examples/js/dbMonApp.js +24 -26
  152. package/examples/js/dbMonAppReact.jsx +79 -0
  153. package/examples/js/dbMonAppReactive.js +28 -0
  154. package/examples/js/fiberDemo.js +4 -4
  155. package/examples/js/filtersDemo.js +8 -8
  156. package/examples/js/forOfDemo.js +7 -9
  157. package/examples/js/forOfDemoComplex.js +44 -17
  158. package/examples/js/form.js +44 -12
  159. package/examples/js/globalConfig.js +117 -0
  160. package/examples/js/ifBindingDemo.js +16 -16
  161. package/examples/js/reactiveDemo.js +119 -0
  162. package/examples/js/switchBindingDemo.js +8 -8
  163. package/examples/react-dbmonster/dist/bundle.js +43 -0
  164. package/examples/react-dbmonster/package-lock.json +537 -0
  165. package/examples/react-dbmonster/package.json +16 -0
  166. package/examples/react-dbmonster/src/index.jsx +80 -0
  167. package/examples/reactiveDemo.html +127 -0
  168. package/examples/refreshRateTest.html +75 -75
  169. package/index.html +841 -0
  170. package/package.json +31 -34
  171. package/rollup.config.js +79 -36
  172. package/src/{_escape.js → _escape.ts} +19 -17
  173. package/src/applyBinding.ts +179 -0
  174. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  175. package/src/binder.ts +289 -0
  176. package/src/changeBinding.ts +93 -0
  177. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  178. package/src/config.ts +107 -0
  179. package/src/createBindingOption.ts +91 -0
  180. package/src/createEventBinding.ts +88 -0
  181. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  182. package/src/{domWalker.js → domWalker.ts} +44 -30
  183. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  184. package/src/hoverBinding.ts +84 -0
  185. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  186. package/src/index.ts +53 -0
  187. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  188. package/src/postProcess.ts +22 -0
  189. package/src/{pubSub.js → pubSub.ts} +24 -15
  190. package/src/reactiveProxy.ts +285 -0
  191. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +55 -33
  192. package/src/{renderIfBinding.js → renderIfBinding.ts} +45 -20
  193. package/src/renderIteration.ts +53 -0
  194. package/src/renderTemplate.ts +165 -0
  195. package/src/renderTemplatesBinding.ts +73 -0
  196. package/src/{showBinding.js → showBinding.ts} +4 -3
  197. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  198. package/src/{textBinding.js → textBinding.ts} +5 -4
  199. package/src/types.ts +124 -0
  200. package/src/util.ts +810 -0
  201. package/test/css/reporter.css +9 -9
  202. package/test/fixtures/dataBindBootstrap.html +2 -2
  203. package/test/fixtures/formBindings.html +9 -1
  204. package/test/globals.d.ts +19 -0
  205. package/test/helpers/testHelper.js +46 -11
  206. package/test/mocks/featureAdsResult.json +65 -65
  207. package/test/mocks/searchResult.json +57 -57
  208. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  209. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  210. package/test/specs/blurBinding.spec.ts +60 -0
  211. package/test/specs/chainableUse.spec.ts +125 -0
  212. package/test/specs/clickBinding.spec.ts +194 -0
  213. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  214. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  215. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  216. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  217. package/test/specs/formBinding.spec.ts +272 -0
  218. package/test/specs/ifBinding.spec.ts +165 -0
  219. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  220. package/test/specs/reactiveProxy.spec.ts +465 -0
  221. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  222. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  223. package/test/specs/templateBinding.spec.ts +273 -0
  224. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  225. package/test/tsconfig.json +31 -0
  226. package/test-output.txt +200 -0
  227. package/test-reactive.html +224 -0
  228. package/tsconfig.json +28 -0
  229. package/vendors/lodash.custom.js +4577 -4577
  230. package/vendors/lodash.custom.min.js +45 -45
  231. package/vitest.config.js +27 -0
  232. package/.eslintrc.js +0 -1
  233. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  234. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  235. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  236. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  237. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  238. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  239. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  240. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  241. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  242. package/coverage/coverage.json +0 -1
  243. package/coverage/lcov/lcov-report/base.css +0 -213
  244. package/coverage/lcov/lcov-report/index.html +0 -93
  245. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  246. package/coverage/lcov/lcov-report/js/index.html +0 -93
  247. package/coverage/lcov/lcov-report/prettify.css +0 -1
  248. package/coverage/lcov/lcov-report/prettify.js +0 -1
  249. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  250. package/coverage/lcov/lcov-report/sorter.js +0 -158
  251. package/coverage/lcov/lcov.info +0 -1991
  252. package/eslintrc.json +0 -40
  253. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  254. package/examples/bootstrap/js/popper.min.js +0 -5
  255. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  256. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  257. package/gruntfile.js +0 -92
  258. package/gulpfile.js +0 -32
  259. package/src/binder.js +0 -422
  260. package/src/changeBinding.js +0 -57
  261. package/src/config.js +0 -65
  262. package/src/createBindingOption.js +0 -66
  263. package/src/createEventBinding.js +0 -46
  264. package/src/eventSystem.js +0 -46
  265. package/src/hoverBinding.js +0 -57
  266. package/src/index.js +0 -26
  267. package/src/renderTemplate.js +0 -128
  268. package/src/util.js +0 -648
  269. package/test/specs/blurBinding.spec.js +0 -57
  270. package/test/specs/formBinding.spec.js +0 -292
  271. package/test/specs/ifBinding.spec.js +0 -169
  272. package/test/specs/templateBinding.spec.js +0 -117
  273. package/vendors/jasmine-jquery.js +0 -841
  274. package/vendors/jquery-3.2.1.min.js +0 -4
@@ -0,0 +1,272 @@
1
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+ describe('Given form-component initised', () => {
5
+ const getElementAttributesObj = function ($el: Element) {
6
+ const obj: any = {};
7
+ Array.from($el.attributes).forEach((attr) => {
8
+ if (attr.specified) {
9
+ obj[attr.name] = attr.value;
10
+ }
11
+ });
12
+ return obj;
13
+ };
14
+
15
+ const formComponentVM = {
16
+ title: 'form component title',
17
+ description: 'form component description',
18
+ carName: 'saab',
19
+ showContent: false,
20
+ markAllCompleted: false,
21
+ gender: 'female',
22
+ testDate: '2017-12-25',
23
+ testRange: '1',
24
+ message: 'This is message from viewModel',
25
+ taskName: '',
26
+ showTaskNameInput: false,
27
+ testAttr: {
28
+ id: 'testId',
29
+ rel: 'testRel',
30
+ class: 'show',
31
+ },
32
+ onAddTask(_e: Event, _$element: any, newValue: any, _oldValue: any) {
33
+ this.taskName = newValue;
34
+ this.updateView();
35
+ },
36
+ onMarkAllCompleted(_e: Event, _$element: any, _newValue: any, _oldValue: any) {
37
+ this.markAllCompleted = true;
38
+ this.updateView();
39
+ },
40
+ onSelected(e: Event, $element: any, newValue: any, oldValue: any) {
41
+ expect(newValue).not.toBe(oldValue);
42
+ },
43
+ onGenderChanged(e: Event, $element: any, newValue: any, oldValue: any) {
44
+ expect(newValue).not.toBe(oldValue);
45
+ },
46
+ onTestDateChanged(e: Event, $element: any, newValue: any, oldValue: any) {
47
+ expect(newValue).not.toBe(oldValue);
48
+ },
49
+ onTestRangeChanged(e: Event, $element: any, newValue: any, oldValue: any) {
50
+ expect(newValue).not.toBe(oldValue);
51
+ this.updateView();
52
+ },
53
+ onTestRangeInputChange(e: Event, $element: any, newValue: any, oldValue: any) {
54
+ expect(newValue).not.toBe(oldValue);
55
+ this.updateView();
56
+ },
57
+ onMessageChanged(e: Event, $element: any, newValue: any, oldValue: any) {
58
+ expect(newValue).not.toBe(oldValue);
59
+ this.updateView();
60
+ },
61
+ onEditTask(_e: Event, _$element: any) {
62
+ this.showTaskNameInput = true;
63
+ this.updateView();
64
+ },
65
+ onFocusEditTask(e: Event, $element: any) {
66
+ expect($element.id).toBe('taskName');
67
+ },
68
+ onBlurEditTask(e: Event, $element: any) {
69
+ expect($element.id).toBe('taskName');
70
+ },
71
+ onTestFormSubmit(e: Event, _$element: any, _formData: any) {
72
+ e.preventDefault();
73
+ },
74
+ updateView() {
75
+ namespace.formComponentApp.render();
76
+ },
77
+ };
78
+ const namespace: any = {};
79
+
80
+ beforeEach(async () => {
81
+ loadFixture('test/fixtures/formBindings.html');
82
+ namespace.formComponentApp = dataBind.init(document.querySelector('[data-bind-comp="form-component"]'), formComponentVM);
83
+ await namespace.formComponentApp.render();
84
+ });
85
+
86
+ afterEach(() => {
87
+ // clean up app
88
+ delete namespace.formComponentApp;
89
+ });
90
+
91
+ it('Then each bond input element should updated according to viewModel', async () => {
92
+ await waitFor(() => {
93
+ expect(document.querySelector('#test-form-title')!.textContent).toBe(formComponentVM.title);
94
+ expect(document.querySelector('#test-form-description')!.textContent).toBe(formComponentVM.description);
95
+ expect((document.querySelector('#test-form-description') as HTMLElement)!.style.display !== 'none').toBe(formComponentVM.showContent);
96
+ expect((document.querySelector('#toggle-all') as HTMLInputElement).checked).toBe(formComponentVM.markAllCompleted);
97
+ expect((document.querySelector('input[name="gender"]:checked') as HTMLInputElement).value).toBe(formComponentVM.gender);
98
+ expect((document.querySelector('#carName') as HTMLSelectElement).value).toBe(formComponentVM.carName);
99
+ expect((document.querySelector('#testDate') as HTMLInputElement).value).toBe(formComponentVM.testDate);
100
+ expect((document.querySelector('#testRange') as HTMLInputElement).value).toBe(formComponentVM.testRange);
101
+ expect(document.querySelector('#testRangeLabel')!.textContent).toBe((document.querySelector('#testRange') as HTMLInputElement).value);
102
+ expect((document.querySelector('#taskName') as HTMLInputElement).value).toBe(formComponentVM.taskName);
103
+ expect((document.querySelector('#taskName') as HTMLElement)!.style.display !== 'none').toBe(formComponentVM.showTaskNameInput);
104
+ }, {timeout: 500});
105
+ });
106
+
107
+
108
+ it('When change #new-todo input value then viewModel should have updated', async () => {
109
+ const task1 = 'new test task';
110
+
111
+ // Wait for element to be ready
112
+ await waitFor(() => {
113
+ const $newTodo = document.getElementById('new-todo');
114
+ expect($newTodo).not.toBeNull();
115
+ }, {timeout: 500});
116
+
117
+ const $newTodo = document.getElementById('new-todo') as HTMLInputElement;
118
+ const evt = document.createEvent('HTMLEvents');
119
+
120
+ evt.initEvent('change', true, true);
121
+ $newTodo.value = task1;
122
+ $newTodo.dispatchEvent(evt);
123
+
124
+ // defer to check after async render
125
+ await waitFor(() => {
126
+ expect(formComponentVM.taskName).toBe(task1);
127
+ expect((document.querySelector('#taskName') as HTMLInputElement).value).toBe(formComponentVM.taskName);
128
+ }, {timeout: 500});
129
+ });
130
+
131
+ it('When #toggle-all checked then viewModel should have updated', async () => {
132
+ const $toggleAll = document.getElementById('toggle-all') as HTMLInputElement;
133
+ const evt = document.createEvent('HTMLEvents');
134
+
135
+ evt.initEvent('change', true, true);
136
+ $toggleAll.checked = true;
137
+ $toggleAll.dispatchEvent(evt);
138
+
139
+ // defer to check after async render
140
+ await waitFor(() => {
141
+ expect(formComponentVM.markAllCompleted).toBe(true);
142
+ }, {timeout: 500});
143
+ });
144
+
145
+ it('When #carName dropdwon changed then viewModel should have updated', async () => {
146
+ const newCarName = 'volvo';
147
+ const $carName = document.getElementById('carName') as HTMLSelectElement;
148
+ const evt = document.createEvent('HTMLEvents');
149
+
150
+ evt.initEvent('change', true, true);
151
+ $carName.value = newCarName;
152
+ $carName.dispatchEvent(evt);
153
+
154
+ await waitFor(() => {
155
+ expect(formComponentVM.carName).toBe(newCarName);
156
+ }, {timeout: 500});
157
+ });
158
+
159
+ it('When #radioMale changed then viewModel should have updated', async () => {
160
+ const newGender = 'male';
161
+ const radioMale = document.getElementById('radioMale') as HTMLInputElement;
162
+ const evt = document.createEvent('HTMLEvents');
163
+
164
+ evt.initEvent('change', true, true);
165
+ radioMale.checked = true;
166
+ radioMale.dispatchEvent(evt);
167
+
168
+ await waitFor(() => {
169
+ expect(formComponentVM.gender).toBe(newGender);
170
+ }, {timeout: 500});
171
+ });
172
+
173
+ it('When #testDate date input changed then viewModel should have updated', async () => {
174
+ const newTestDate = '2017-12-31';
175
+ const $testDate = document.getElementById('testDate') as HTMLInputElement;
176
+ const evt = document.createEvent('HTMLEvents');
177
+
178
+ evt.initEvent('change', true, true);
179
+ $testDate.value = newTestDate;
180
+ $testDate.dispatchEvent(evt);
181
+
182
+ await waitFor(() => {
183
+ expect(formComponentVM.testDate).toBe(newTestDate);
184
+ }, {timeout: 500});
185
+ });
186
+
187
+ it('When #testRange range input changed then viewModel should have updated', async () => {
188
+ const newTestRange = '3';
189
+ const $testRange = document.getElementById('testRange') as HTMLInputElement;
190
+ const evt = document.createEvent('HTMLEvents');
191
+
192
+ evt.initEvent('change', true, true);
193
+ $testRange.value = newTestRange;
194
+ $testRange.dispatchEvent(evt);
195
+
196
+ await waitFor(() => {
197
+ expect(formComponentVM.testRange).toBe(newTestRange);
198
+ expect(document.querySelector('#testRangeLabel')!.textContent).toBe(newTestRange);
199
+ }, {timeout: 500});
200
+ });
201
+
202
+ it('When #testRange range input trigger onInput changed then viewModel should have updated', async () => {
203
+ const newTestRange = '4';
204
+ const $testRange = document.getElementById('testRange') as HTMLInputElement;
205
+ const evt = document.createEvent('HTMLEvents');
206
+
207
+ evt.initEvent('input', true, true);
208
+ $testRange.value = newTestRange;
209
+ $testRange.dispatchEvent(evt);
210
+
211
+ await waitFor(() => {
212
+ expect(formComponentVM.testRange).toBe(newTestRange);
213
+ expect(document.querySelector('#testRangeLabel')!.textContent).toBe(newTestRange);
214
+ }, {timeout: 500});
215
+ });
216
+
217
+ it('When #message range input changed with xss html then viewModel data should have escaped value and updated', async () => {
218
+ const newMessage = '<img src=x onerror=alert(1)>';
219
+ const escapedMessage = '&lt;img src=x onerror=alert(1)&gt;';
220
+ const $message = document.getElementById('message') as HTMLTextAreaElement;
221
+ const evt = document.createEvent('HTMLEvents');
222
+
223
+ evt.initEvent('change', true, true);
224
+ $message.value = newMessage;
225
+ $message.dispatchEvent(evt);
226
+
227
+ await waitFor(() => {
228
+ expect(formComponentVM.message).toBe(escapedMessage);
229
+ }, {timeout: 500});
230
+ });
231
+
232
+ it('When #labelTaskeName label double clicked then viewModel should have updated and #taskName should show', async () => {
233
+ const $taskName = document.getElementById('taskName') as HTMLInputElement;
234
+
235
+ await waitFor(() => {
236
+ expect((document.querySelector('#taskName') as HTMLElement)!.style.display !== 'none').toBe(formComponentVM.showTaskNameInput);
237
+ }, {timeout: 500});
238
+
239
+ $taskName.focus();
240
+ $taskName.blur();
241
+ });
242
+
243
+ describe('Element with attribute binding', () => {
244
+ it('should display attribute from viewModel', async () => {
245
+ const $el = document.querySelector('[data-bind-attr="testAttr"]')!;
246
+ await waitFor(() => {
247
+ const attrObj = getElementAttributesObj($el);
248
+ Object.keys(formComponentVM.testAttr).forEach(k => {
249
+ expect(attrObj[k]).toBe((formComponentVM.testAttr as any)[k]);
250
+ });
251
+ }, {timeout: 500});
252
+ });
253
+
254
+ it('should update attribute according to viewModel', async () => {
255
+ const $el = document.querySelector('[data-bind-attr="testAttr"]')!;
256
+ // Use component.viewModel for reactive updates (reactive mode is default)
257
+ namespace.formComponentApp.viewModel.testAttr = {
258
+ id: '8888',
259
+ class: 'hidden',
260
+ } as any;
261
+ // No need to call updateView() - reactive mode triggers automatic render
262
+
263
+ await waitFor(() => {
264
+ const attrObj = getElementAttributesObj($el);
265
+ Object.keys(namespace.formComponentApp.viewModel.testAttr).forEach(k => {
266
+ expect(attrObj[k]).toBe((namespace.formComponentApp.viewModel.testAttr as any)[k]);
267
+ });
268
+ expect(attrObj.rel).toBeUndefined();
269
+ }, {timeout: 500});
270
+ });
271
+ });
272
+ });
@@ -0,0 +1,165 @@
1
+ import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+
5
+ describe('Given [data-bind-comp="if-component"] inited', () => {
6
+ const namespace: any = {};
7
+
8
+ beforeEach(async () => {
9
+ loadFixture('test/fixtures/ifBinding.html');
10
+
11
+ namespace.viewModel = {
12
+ renderIntro: true,
13
+ heading: 'Test data-if-binding',
14
+ description: 'This is intro text',
15
+ story: {
16
+ title: 'Hansel and Gretel',
17
+ description:
18
+ '"Hansel and Gretel" (also known as Hansel and Grettel, Hansel and Grethel, or Little Brother and Little Sister) is a well-known fairy tale of German origin.',
19
+ link: 'https://www.google.com.au/search?q=Hansel+and+Gretel',
20
+ },
21
+ viewModelPropFn($data: any) {
22
+ return typeof $data.viewModelPropFn === 'function';
23
+ },
24
+ undefinedViewModelPropFn(_$data: any) {
25
+ return;
26
+ },
27
+ setStroylinkAttr(_$data: any) {
28
+ return {
29
+ href: this.story.link,
30
+ title: this.story.title,
31
+ target: '_blank',
32
+ rel: 'noopener noreferrer',
33
+ };
34
+ },
35
+ onStoryClick(e: Event, _$el: any) {
36
+ e.preventDefault();
37
+ },
38
+ updateView(opt?: any) {
39
+ this.APP.render(opt);
40
+ },
41
+ };
42
+
43
+ namespace.myIfComponent = dataBind.init(document.querySelector('[data-bind-comp="if-component"]'), namespace.viewModel);
44
+
45
+ await namespace.myIfComponent.render();
46
+
47
+ // vitest spies
48
+ vi.spyOn(namespace.viewModel, 'onStoryClick');
49
+ });
50
+
51
+ afterEach(() => {
52
+ // clean up all app/components
53
+ for (const prop in namespace) {
54
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
55
+ delete namespace[prop];
56
+ }
57
+ }
58
+ });
59
+
60
+ it('Then [data-bind-comp="myIfComponent"] should have render', async () => {
61
+ await waitFor(() => {
62
+ expect(document.querySelector('#intro-heading')!.textContent).toBe(namespace.viewModel.heading);
63
+ expect(document.querySelector('#intro-description')!.textContent).toBe(namespace.viewModel.description);
64
+ }, {timeout: 500});
65
+ });
66
+
67
+ it('Then render if-binding elements with comment tag wrap around', async () => {
68
+ await waitFor(() => {
69
+ const introOpenCommentWrap = document.getElementById('intro')!.previousSibling;
70
+ const introCloseCommentWrap = document.getElementById('intro')!.nextSibling;
71
+
72
+ expect(introOpenCommentWrap!.nodeType).toBe(8);
73
+ expect(introCloseCommentWrap!.nodeType).toBe(8);
74
+ expect(introOpenCommentWrap!.textContent).toContain('data-if');
75
+ expect(introCloseCommentWrap!.textContent).toContain('data-if');
76
+ }, {timeout: 500});
77
+ });
78
+
79
+
80
+ it('should not render #story ', async () => {
81
+ await waitFor(() => {
82
+ expect(document.querySelector('#story')).toBe(null);
83
+ }, {timeout: 500});
84
+ });
85
+
86
+
87
+ it('should not render #testPropFn ', async () => {
88
+ await waitFor(() => {
89
+ expect(document.getElementById('testPropFn')).not.toBe(null);
90
+ }, {timeout: 500});
91
+ });
92
+
93
+
94
+ it('should not render #testUnDefiniedProp ', async () => {
95
+ await waitFor(() => {
96
+ expect(document.getElementById('testUnDefiniedProp')).toBe(null);
97
+ }, {timeout: 500});
98
+ });
99
+
100
+
101
+ it('should render inverse negated boolean block', async () => {
102
+ await waitFor(() => {
103
+ expect(document.getElementById('NotTestUnDefiniedProp')).not.toBe(null);
104
+ }, {timeout: 500});
105
+ });
106
+
107
+ describe('When update viewModel renderIntro to false', () => {
108
+ it('should render story and remove intro', async () => {
109
+ namespace.viewModel.renderIntro = false;
110
+ await namespace.myIfComponent.render();
111
+
112
+ await waitFor(() => {
113
+ expect(document.querySelector('#story')).not.toBe(null);
114
+ expect(document.querySelector('#intro')).toBe(null);
115
+ expect(document.querySelector('#storyIntroHeading')!.textContent).toBe(namespace.viewModel.story.title);
116
+ expect(document.querySelector('#storyDescription')!.textContent).toBe(namespace.viewModel.story.description);
117
+ expect(document.querySelector('#storyLink')!.getAttribute('href')).toBe(namespace.viewModel.story.link);
118
+ expect(document.querySelector('#storyLink')!.getAttribute('title')).toBe(namespace.viewModel.story.title);
119
+ expect(document.querySelector('#storyLink')!.getAttribute('target')).toBe('_blank');
120
+ expect(document.querySelector('#storyLink')!.getAttribute('rel')).toBe('noopener noreferrer');
121
+
122
+ const evt = document.createEvent('HTMLEvents');
123
+ evt.initEvent('click', true, true);
124
+
125
+ const $searchInput = document.getElementById('storyLink')!;
126
+ $searchInput.dispatchEvent(evt);
127
+
128
+ expect(namespace.viewModel.onStoryClick).toHaveBeenCalled();
129
+ namespace.viewModel.onStoryClick.mockClear();
130
+ }, {timeout: 500});
131
+ });
132
+ });
133
+
134
+ describe('When update viewModel renderIntro to true', () => {
135
+ it('should render intro and remove story', async () => {
136
+ namespace.viewModel.renderIntro = true;
137
+ await namespace.myIfComponent.render();
138
+
139
+ await waitFor(() => {
140
+ expect(document.querySelector('#story')).toBe(null);
141
+ expect(document.querySelector('#intro')).not.toBe(null);
142
+ }, {timeout: 500});
143
+ });
144
+ });
145
+
146
+ describe('When update viewModel renderIntro to false again', () => {
147
+ it('should render story and event handler rebind', async () => {
148
+ namespace.viewModel.renderIntro = false;
149
+ await namespace.myIfComponent.render();
150
+
151
+ await waitFor(() => {
152
+ const $storyLink = document.getElementById('storyLink')!;
153
+ const evt = document.createEvent('HTMLEvents');
154
+ evt.initEvent('click', true, true);
155
+
156
+ expect(document.querySelector('#story')).not.toBe(null);
157
+ expect(document.querySelector('#intro')).toBe(null);
158
+
159
+ $storyLink.dispatchEvent(evt);
160
+ expect(namespace.viewModel.onStoryClick).toHaveBeenCalled();
161
+ namespace.viewModel.onStoryClick.mockClear();
162
+ }, {timeout: 500});
163
+ });
164
+ });
165
+ });
@@ -1,88 +1,88 @@
1
- describe('When nested data-bind-comp initised', () => {
2
- const namespace = {};
3
-
4
- jasmine.getFixtures().fixturesPath = 'test';
5
-
6
- beforeEach(function() {
7
- loadFixtures('./fixtures/nestedComponents.html');
8
-
9
- namespace.parentComponentVM = {
10
- title: 'parent component title',
11
- description: 'parent component description',
12
- };
13
-
14
- namespace.childComponentVM = {
15
- title: 'child component title',
16
- description: 'child component description',
17
- };
18
-
19
- namespace.grandChildComponentVM = {
20
- title: 'grand child component title',
21
- description: 'grand child component description',
22
- };
23
-
24
- namespace.slibingChildComponentVM = {
25
- title: 'slibing child component title',
26
- description: 'slibing child component description',
27
- };
28
-
29
- const parentComponent = dataBind.init(document.querySelector('[data-bind-comp="parent-component"]'), namespace.parentComponentVM);
30
- const childComponent = dataBind.init(document.querySelector('[data-bind-comp="child-component"]'), namespace.childComponentVM);
31
- const grandChildComponent = dataBind.init(
32
- document.querySelector('[data-bind-comp="grand-child-component"]'),
33
- namespace.grandChildComponentVM,
34
- );
35
- const slibingChildComponent = dataBind.init(
36
- document.querySelector('[data-bind-comp="slibing-child-component"]'),
37
- namespace.slibingChildComponentVM,
38
- );
39
-
40
- parentComponent.render();
41
- childComponent.render();
42
- grandChildComponent.render();
43
- slibingChildComponent.render();
44
- });
45
-
46
- afterEach(() => {
47
- // clean up all app/components
48
- for (const prop in namespace) {
49
- if (namespace.hasOwnProperty(prop)) {
50
- delete namespace[prop];
51
- }
52
- }
53
- });
54
-
55
- it('Then #parent-component-title and #parent-component-description should render according parentComponentVM', (done) => {
56
- setTimeout(() => {
57
- expect($('#parent-component-title').text()).toBe(namespace.parentComponentVM.title);
58
- expect($('#parent-component-description').text()).toBe(namespace.parentComponentVM.description);
59
- done();
60
- }, 200);
61
- });
62
-
63
- it('Then #child-component-title and #child-component-description should render according childComponentVM', (done) => {
64
- setTimeout(() => {
65
- expect($('#child-component-title').text()).toBe(namespace.childComponentVM.title);
66
- expect($('#child-component-description').text()).toBe(namespace.childComponentVM.description);
67
- done();
68
- }, 200);
69
- });
70
-
71
- it('Then #grand-child-component-title and #grand-child-component-description should render according grandChildComponentVM', (done) => {
72
- setTimeout(() => {
73
- expect($('#grand-child-component-title').text()).toBe(namespace.grandChildComponentVM.title);
74
- expect($('#grand-child-component-description').text()).toBe(namespace.grandChildComponentVM.description);
75
- done();
76
- }, 200);
77
- });
78
-
79
- it('Then #slibing-child-component-title and #slibing-child-component-description should render according slibingChildComponentVM', (done) => {
80
- setTimeout(() => {
81
- expect($('#slibing-child-component-title').text()).toBe(namespace.slibingChildComponentVM.title);
82
- expect($('#slibing-child-component-description').text()).toBe(
83
- namespace.slibingChildComponentVM.description,
84
- );
85
- done();
86
- }, 200);
87
- });
88
- });
1
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+ describe('When nested data-bind-comp initised', () => {
5
+ const namespace: any = {};
6
+
7
+ beforeEach(() => {
8
+ loadFixture('test/fixtures/nestedComponents.html');
9
+
10
+ namespace.parentComponentVM = {
11
+ title: 'parent component title',
12
+ description: 'parent component description',
13
+ };
14
+
15
+ namespace.childComponentVM = {
16
+ title: 'child component title',
17
+ description: 'child component description',
18
+ };
19
+
20
+ namespace.grandChildComponentVM = {
21
+ title: 'grand child component title',
22
+ description: 'grand child component description',
23
+ };
24
+
25
+ namespace.slibingChildComponentVM = {
26
+ title: 'slibing child component title',
27
+ description: 'slibing child component description',
28
+ };
29
+
30
+ const parentComponent = dataBind.init(document.querySelector('[data-bind-comp="parent-component"]'), namespace.parentComponentVM);
31
+ const childComponent = dataBind.init(document.querySelector('[data-bind-comp="child-component"]'), namespace.childComponentVM);
32
+ const grandChildComponent = dataBind.init(
33
+ document.querySelector('[data-bind-comp="grand-child-component"]'),
34
+ namespace.grandChildComponentVM,
35
+ );
36
+ const slibingChildComponent = dataBind.init(
37
+ document.querySelector('[data-bind-comp="slibing-child-component"]'),
38
+ namespace.slibingChildComponentVM,
39
+ );
40
+
41
+ parentComponent.render();
42
+ childComponent.render();
43
+ grandChildComponent.render();
44
+ slibingChildComponent.render();
45
+ });
46
+
47
+ afterEach(() => {
48
+ // clean up all app/components
49
+ for (const prop in namespace) {
50
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
51
+ delete namespace[prop];
52
+ }
53
+ }
54
+ });
55
+
56
+ it('Then #parent-component-title and #parent-component-description should render according parentComponentVM', async () => {
57
+ await waitFor(() => {
58
+ expect(document.querySelector('#parent-component-title')!.textContent).toBe(namespace.parentComponentVM.title);
59
+ expect(document.querySelector('#parent-component-description')!.textContent).toBe(namespace.parentComponentVM.description);
60
+ }, {timeout: 500});
61
+ });
62
+
63
+
64
+ it('Then #child-component-title and #child-component-description should render according childComponentVM', async () => {
65
+ await waitFor(() => {
66
+ expect(document.querySelector('#child-component-title')!.textContent).toBe(namespace.childComponentVM.title);
67
+ expect(document.querySelector('#child-component-description')!.textContent).toBe(namespace.childComponentVM.description);
68
+ }, {timeout: 500});
69
+ });
70
+
71
+
72
+ it('Then #grand-child-component-title and #grand-child-component-description should render according grandChildComponentVM', async () => {
73
+ await waitFor(() => {
74
+ expect(document.querySelector('#grand-child-component-title')!.textContent).toBe(namespace.grandChildComponentVM.title);
75
+ expect(document.querySelector('#grand-child-component-description')!.textContent).toBe(namespace.grandChildComponentVM.description);
76
+ }, {timeout: 500});
77
+ });
78
+
79
+
80
+ it('Then #slibing-child-component-title and #slibing-child-component-description should render according slibingChildComponentVM', async () => {
81
+ await waitFor(() => {
82
+ expect(document.querySelector('#slibing-child-component-title')!.textContent).toBe(namespace.slibingChildComponentVM.title);
83
+ expect(document.querySelector('#slibing-child-component-description')!.textContent).toBe(
84
+ namespace.slibingChildComponentVM.description,
85
+ );
86
+ }, {timeout: 500});
87
+ });
88
+ });