@gogocat/data-bind 1.12.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 (271) 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 +2756 -2530
  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/globalConfig.html +131 -0
  147. package/examples/js/afterRenderDemo.js +190 -0
  148. package/examples/js/appTodo.js +46 -46
  149. package/examples/js/attrBindingDemo.js +2 -2
  150. package/examples/js/dbMonApp.js +24 -26
  151. package/examples/js/dbMonAppReact.jsx +79 -0
  152. package/examples/js/dbMonAppReactive.js +28 -0
  153. package/examples/js/fiberDemo.js +4 -4
  154. package/examples/js/filtersDemo.js +8 -8
  155. package/examples/js/forOfDemo.js +7 -9
  156. package/examples/js/forOfDemoComplex.js +44 -17
  157. package/examples/js/form.js +14 -14
  158. package/examples/js/globalConfig.js +117 -0
  159. package/examples/js/ifBindingDemo.js +16 -16
  160. package/examples/js/reactiveDemo.js +119 -0
  161. package/examples/js/switchBindingDemo.js +8 -8
  162. package/examples/react-dbmonster/dist/bundle.js +43 -0
  163. package/examples/react-dbmonster/package-lock.json +537 -0
  164. package/examples/react-dbmonster/package.json +16 -0
  165. package/examples/react-dbmonster/src/index.jsx +80 -0
  166. package/examples/reactiveDemo.html +127 -0
  167. package/examples/refreshRateTest.html +75 -75
  168. package/index.html +841 -0
  169. package/package.json +31 -34
  170. package/rollup.config.js +79 -36
  171. package/src/{_escape.js → _escape.ts} +19 -17
  172. package/src/{applyBinding.js → applyBinding.ts} +27 -18
  173. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  174. package/src/{binder.js → binder.ts} +289 -181
  175. package/src/changeBinding.ts +93 -0
  176. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  177. package/src/config.ts +107 -0
  178. package/src/{createBindingOption.js → createBindingOption.ts} +39 -15
  179. package/src/createEventBinding.ts +88 -0
  180. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  181. package/src/{domWalker.js → domWalker.ts} +44 -30
  182. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  183. package/src/hoverBinding.ts +84 -0
  184. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  185. package/src/index.ts +53 -0
  186. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  187. package/src/{postProcess.js → postProcess.ts} +6 -4
  188. package/src/{pubSub.js → pubSub.ts} +24 -21
  189. package/src/reactiveProxy.ts +285 -0
  190. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +54 -32
  191. package/src/{renderIfBinding.js → renderIfBinding.ts} +41 -19
  192. package/src/{renderIteration.js → renderIteration.ts} +24 -8
  193. package/src/renderTemplate.ts +165 -0
  194. package/src/renderTemplatesBinding.ts +73 -0
  195. package/src/{showBinding.js → showBinding.ts} +4 -3
  196. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  197. package/src/{textBinding.js → textBinding.ts} +5 -4
  198. package/src/types.ts +124 -0
  199. package/src/util.ts +810 -0
  200. package/test/css/reporter.css +9 -9
  201. package/test/globals.d.ts +19 -0
  202. package/test/helpers/testHelper.js +46 -11
  203. package/test/mocks/featureAdsResult.json +65 -65
  204. package/test/mocks/searchResult.json +57 -57
  205. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  206. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  207. package/test/specs/blurBinding.spec.ts +60 -0
  208. package/test/specs/chainableUse.spec.ts +125 -0
  209. package/test/specs/clickBinding.spec.ts +194 -0
  210. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  211. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  212. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  213. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  214. package/test/specs/formBinding.spec.ts +272 -0
  215. package/test/specs/ifBinding.spec.ts +165 -0
  216. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  217. package/test/specs/reactiveProxy.spec.ts +465 -0
  218. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  219. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  220. package/test/specs/templateBinding.spec.ts +273 -0
  221. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  222. package/test/tsconfig.json +31 -0
  223. package/test-output.txt +200 -0
  224. package/test-reactive.html +224 -0
  225. package/tsconfig.json +28 -0
  226. package/vendors/lodash.custom.js +4577 -4577
  227. package/vendors/lodash.custom.min.js +45 -45
  228. package/vitest.config.js +27 -0
  229. package/.eslintrc.js +0 -1
  230. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  231. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  232. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  233. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  234. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  235. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  236. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  237. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  238. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  239. package/coverage/coverage.json +0 -1
  240. package/coverage/lcov/lcov-report/base.css +0 -213
  241. package/coverage/lcov/lcov-report/index.html +0 -93
  242. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  243. package/coverage/lcov/lcov-report/js/index.html +0 -93
  244. package/coverage/lcov/lcov-report/prettify.css +0 -1
  245. package/coverage/lcov/lcov-report/prettify.js +0 -1
  246. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  247. package/coverage/lcov/lcov-report/sorter.js +0 -158
  248. package/coverage/lcov/lcov.info +0 -1991
  249. package/eslintrc.json +0 -40
  250. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  251. package/examples/bootstrap/js/popper.min.js +0 -5
  252. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  253. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  254. package/gruntfile.js +0 -92
  255. package/gulpfile.js +0 -32
  256. package/src/applyBindingExport.js +0 -5
  257. package/src/changeBinding.js +0 -63
  258. package/src/config.js +0 -66
  259. package/src/createEventBinding.js +0 -46
  260. package/src/eventSystem.js +0 -46
  261. package/src/hoverBinding.js +0 -57
  262. package/src/index.js +0 -26
  263. package/src/renderTemplate.js +0 -128
  264. package/src/renderTemplatesBinding.js +0 -44
  265. package/src/util.js +0 -648
  266. package/test/specs/blurBinding.spec.js +0 -57
  267. package/test/specs/formBinding.spec.js +0 -316
  268. package/test/specs/ifBinding.spec.js +0 -169
  269. package/test/specs/templateBinding.spec.js +0 -117
  270. package/vendors/jasmine-jquery.js +0 -841
  271. 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
+ });