@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
@@ -1,76 +1,75 @@
1
- /* eslint-disable max-len */
2
- describe('Given [data-bind-comp="filter-component"] inited', () => {
3
- const namespace = {};
4
-
5
- jasmine.getFixtures().fixturesPath = 'test';
6
-
7
- beforeEach(function(done) {
8
- loadFixtures('./fixtures/filters.html');
9
-
10
- namespace.viewModel = {
11
- renderIntro: false,
12
- heading: 'Test data-if-binding',
13
- description: 'This is intro text',
14
- discountRate: 0.8,
15
- gstRate: 1.1,
16
- story: {
17
- title: 'Hansel and Gretel',
18
- description:
19
- '"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.',
20
- link: 'https://www.google.com.au/search?q=Hansel+and+Gretel',
21
- price: 100,
22
- },
23
- toDiscount: function(value) {
24
- return Number(value) * this.discountRate;
25
- },
26
- addGst: function(value) {
27
- return Number(value) * this.gstRate;
28
- },
29
- updateView: function(opt) {
30
- return this.APP.render(opt);
31
- },
32
- };
33
-
34
- namespace.filterComponent = dataBind.init(document.querySelector('[data-bind-comp="filter-component"]'), namespace.viewModel);
35
- namespace.filterComponent.render().then(() => done());
36
- });
37
-
38
- afterEach(() => {
39
- // clean up all app/components
40
- for (const prop in namespace) {
41
- if (namespace.hasOwnProperty(prop)) {
42
- delete namespace[prop];
43
- }
44
- }
45
- });
46
-
47
- it('Then [data-bind-comp="filter-component"] should render story with once filter and not intro', () => {
48
- const $intro = document.getElementById('intro');
49
- const $story = document.getElementById('story');
50
-
51
- expect($intro).toBe(null);
52
- expect($story).not.toBe(null);
53
- expect($story.firstElementChild).not.toBe(null);
54
- });
55
-
56
- it('Should render intro but not story section after update viewModel', (done) => {
57
- namespace.filterComponent.viewModel.renderIntro = true;
58
- namespace.filterComponent.viewModel.updateView().then(() => {
59
- const $intro = document.getElementById('intro');
60
- const $story = document.getElementById('story');
61
- expect($intro).toBe(null);
62
- expect($story).toBe(null);
63
- done();
64
- });
65
- });
66
-
67
- it('Should render story and stroyPrice pass through filters | toDiscount | addGst', () => {
68
- const $story = document.getElementById('story');
69
- const stroyPrice = document.getElementById('stroyPrice').textContent;
70
- const discountedPrice = namespace.viewModel.toDiscount(namespace.viewModel.story.price);
71
- const finalPrice = namespace.viewModel.addGst(discountedPrice);
72
-
73
- expect($story).not.toBe(null);
74
- expect(Number(stroyPrice)).toBe(finalPrice);
75
- });
76
- });
1
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
+
3
+
4
+ describe('Given [data-bind-comp="filter-component"] inited', () => {
5
+ const namespace: any = {};
6
+
7
+ beforeEach(async () => {
8
+ loadFixture('test/fixtures/filters.html');
9
+
10
+ namespace.viewModel = {
11
+ renderIntro: false,
12
+ heading: 'Test data-if-binding',
13
+ description: 'This is intro text',
14
+ discountRate: 0.8,
15
+ gstRate: 1.1,
16
+ story: {
17
+ title: 'Hansel and Gretel',
18
+ description:
19
+ '"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.',
20
+ link: 'https://www.google.com.au/search?q=Hansel+and+Gretel',
21
+ price: 100,
22
+ },
23
+ toDiscount(value: any) {
24
+ return Number(value) * this.discountRate;
25
+ },
26
+ addGst(value: any) {
27
+ return Number(value) * this.gstRate;
28
+ },
29
+ updateView(opt?: any) {
30
+ return this.APP.render(opt);
31
+ },
32
+ };
33
+
34
+ namespace.filterComponent = dataBind.init(document.querySelector('[data-bind-comp="filter-component"]'), namespace.viewModel);
35
+ await namespace.filterComponent.render();
36
+ });
37
+
38
+ afterEach(() => {
39
+ // clean up all app/components
40
+ for (const prop in namespace) {
41
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
42
+ delete namespace[prop];
43
+ }
44
+ }
45
+ });
46
+
47
+ it('Then [data-bind-comp="filter-component"] should render story with once filter and not intro', () => {
48
+ const $intro = document.getElementById('intro');
49
+ const $story = document.getElementById('story');
50
+
51
+ expect($intro).toBe(null);
52
+ expect($story).not.toBe(null);
53
+ expect($story!.firstElementChild).not.toBe(null);
54
+ });
55
+
56
+ it('Should render intro but not story section after update viewModel', async () => {
57
+ namespace.filterComponent.viewModel.renderIntro = true;
58
+ await namespace.filterComponent.viewModel.updateView();
59
+
60
+ const $intro = document.getElementById('intro');
61
+ const $story = document.getElementById('story');
62
+ expect($intro).toBe(null);
63
+ expect($story).toBe(null);
64
+ });
65
+
66
+ it('Should render story and stroyPrice pass through filters | toDiscount | addGst', () => {
67
+ const $story = document.getElementById('story');
68
+ const stroyPrice = document.getElementById('stroyPrice')!.textContent!;
69
+ const discountedPrice = namespace.viewModel.toDiscount(namespace.viewModel.story.price);
70
+ const finalPrice = namespace.viewModel.addGst(discountedPrice);
71
+
72
+ expect($story).not.toBe(null);
73
+ expect(Number(stroyPrice)).toBe(finalPrice);
74
+ });
75
+ });
@@ -1,219 +1,208 @@
1
- /* eslint-disable max-len */
2
- // It seems PhantomJS has issue with createComment and createRange
3
- const isEnvSupportDocRange = ((document) => {
4
- let docRange;
5
- let commentNode;
6
- const commentText = 'x';
7
- let ret = true;
8
-
9
- if (typeof document.createRange !== 'function') {
10
- return (ret = false);
11
- }
12
- try {
13
- docRange = document.createRange();
14
- docRange.deleteContents();
15
- commentNode = document.createComment(commentText);
16
- if (commentNode.nodeType !== 8 || commentNode.textContent !== commentText) {
17
- return (ret = false);
18
- }
19
- } catch (err) {
20
- return (ret = false);
21
- }
22
- return ret;
23
- })(document);
24
-
25
- describe('When search-results-component with forOf binding inited', () => {
26
- const namespace = {};
27
-
28
- jasmine.getFixtures().fixturesPath = 'test';
29
-
30
- beforeEach(() => {
31
- loadFixtures('./fixtures/forOfBinding.html');
32
-
33
- namespace.viewModel = {
34
- searchResultTitle: 'Featured service providers',
35
- messageTriggerCss: '',
36
- bookmarkCss: '',
37
- searchResults: [
38
- {
39
- id: '001',
40
- title: 'Card title that wraps to a new line',
41
- description:
42
- 'This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.',
43
- image: 'bootstrap/images/pic-home.jpg',
44
- bookmarked: false,
45
- numLikes: 110,
46
- options: [{text: '1', value: '1'}, {text: '2', value: '2'}, {text: '3', value: '3'}],
47
- },
48
- {
49
- id: '456',
50
- title: 'Card title',
51
- description: 'This card has supporting text below as a natural lead-in to additional content.',
52
- image: '',
53
- bookmarked: false,
54
- numLikes: 8,
55
- selected: true,
56
- options: [{text: '4', value: '4'}, {text: '5', value: '5'}, {text: '6', value: '6'}],
57
- },
58
- {
59
- id: '789',
60
- title: 'Sample carpemter service',
61
- description:
62
- 'This is a wider card with supporting text below as a natural lead-in to additional content. This card has even longer content than the first to show that equal height action.',
63
- image: 'bootstrap/images/pic-carpenter.jpg',
64
- bookmarked: false,
65
- numLikes: 8,
66
- highlight: true,
67
- highlightCss: 'result-item--highlight',
68
- options: [{text: '7', value: '7'}, {text: '8', value: '8'}, {text: '9', value: '9'}],
69
- },
70
- ],
71
- getResultItemAttr: function(index, oldAttrObj, $el) {
72
- const self = this;
73
- if (self.searchResults[index].image) {
74
- return {
75
- src: self.searchResults[index].image,
76
- alt: self.searchResults[index].title || '',
77
- };
78
- }
79
- },
80
- setResultOptionAttr: function($data, oldAttrObj, $el) {
81
- if ($data && $data.value) {
82
- // todo: the index here is the outter loop index
83
- return {
84
- value: $data.value,
85
- };
86
- }
87
- },
88
- onAdMessageCheck: function(e, $el, newValue, oldValue, index) {
89
- console.log('onAdMessageCheck: ', $el, newValue, oldValue, index);
90
- },
91
- onAdBookmarkClick: function(e, $el, index) {
92
- e.preventDefault();
93
- console.log('onAdBookmarkClick: ', $el, index);
94
- },
95
- };
96
-
97
- namespace.searchResultsComponent = dataBind.init(
98
- document.querySelector('[data-bind-comp="search-results-component"]'),
99
- namespace.viewModel,
100
- );
101
-
102
- namespace.searchResultsComponent.render();
103
- });
104
-
105
- afterEach(() => {
106
- // clean up all app/components
107
- for (const prop in namespace) {
108
- if (namespace.hasOwnProperty(prop)) {
109
- delete namespace[prop];
110
- }
111
- }
112
- });
113
-
114
- it('Then [data-bind-comp="search-results-component"] should have render', (done) => {
115
- // skip if test environment doesn't support document.createRange
116
- if (!isEnvSupportDocRange) {
117
- expect(isEnvSupportDocRange).toBe(false);
118
- done();
119
- return;
120
- }
121
- setTimeout(() => {
122
- expect($('#searchResultTitle').text()).toBe(namespace.viewModel.searchResultTitle);
123
- // expect($('#search-result-columns').children().length).not.toBe(0);
124
- done();
125
- }, 200);
126
- });
127
-
128
- it('Then render forOf binding elements with comment tag wrap around', (done) => {
129
- setTimeout(() => {
130
- const $searchColumn = document.getElementById('search-result-columns');
131
- // not sure why jasmine first execution before render complete, that's why element doesn't exsits
132
- // but when run just this spec it will works
133
- if (!$searchColumn.firstElementChild) {
134
- expect($searchColumn.firstElementChild).toBe(null);
135
- done();
136
- return;
137
- }
138
- const firstCommentWrap = $searchColumn.firstElementChild.previousSibling;
139
- const lastCommentWrap = $searchColumn.lastElementChild.nextSibling;
140
-
141
- expect(firstCommentWrap.nodeType).toBe(8);
142
- expect(lastCommentWrap.nodeType).toBe(8);
143
- expect(firstCommentWrap.textContent).toContain('data-forOf');
144
- expect(lastCommentWrap.textContent).toContain('data-forOf');
145
- done();
146
- }, 200);
147
- });
148
-
149
- it('Then render same amount of items in viewModel.searchResults', (done) => {
150
- setTimeout(() => {
151
- const $searchColumn = document.getElementById('search-result-columns');
152
- // not sure why jasmine first execution before render complete, that's why element doesn't exsits
153
- // but when run just this spec it will works
154
- if (!$searchColumn.firstElementChild) {
155
- expect($searchColumn.firstElementChild).toBe(null);
156
- done();
157
- return;
158
- }
159
- expect($searchColumn.children.length).toBe(namespace.viewModel.searchResults.length);
160
- done();
161
- }, 200);
162
- });
163
-
164
- describe('When each search item rendered', () => {
165
- it('should render bindings according to searchResults data', (done) => {
166
- if (!isEnvSupportDocRange) {
167
- expect(isEnvSupportDocRange).toBe(false);
168
- done();
169
- return;
170
- }
171
- setTimeout(() => {
172
- const $results = $('#search-result-columns').children();
173
- // not sure why jasmine first execution before render complete, that's why element doesn't exsits
174
- // but when run just this spec it will works
175
- if (!$results.length) {
176
- expect($results.length).toBe(0);
177
- done();
178
- return;
179
- }
180
-
181
- expect($results.length).not.toBe(0);
182
-
183
- $results.each(function(index) {
184
- const indexString = String(index);
185
- const $result = $(this);
186
- const $img = $result.find('.result-item__img');
187
- const $body = $result.find('.card-body');
188
- const $footer = $result.find('.result-item__footer');
189
- const $checkbox = $footer.find('.result-item__icon-checkbox');
190
- const $options = $footer.find('select.form-control option');
191
- const imgSrc = $img.attr('src') || '';
192
- let bodyIndex = $body.find('.bodyIndex').text();
193
- let footerIndex = $footer.find('.footerIndex').text();
194
- let bookMarkIndex = $result.find('.bookMarkIndex').text();
195
- const searchResult = namespace.viewModel.searchResults[index];
196
-
197
- bodyIndex = bodyIndex.charAt(bodyIndex.length - 1);
198
- footerIndex = footerIndex.charAt(footerIndex.length - 1);
199
- bookMarkIndex = bookMarkIndex.charAt(bookMarkIndex.length - 1);
200
-
201
- expect($img.length).not.toBe(0);
202
- expect(imgSrc).toBe(namespace.viewModel.searchResults[index].image);
203
- expect($body.children().length).not.toBe(0);
204
- expect(bodyIndex).toEqual(indexString);
205
- expect($footer.length).not.toBe(0);
206
- expect(footerIndex).toEqual(indexString);
207
- expect(bookMarkIndex).toEqual(indexString);
208
- expect($checkbox[0].checked).toEqual(Boolean(searchResult.selected));
209
- // first option is not from data
210
- expect($options.length).toEqual(searchResult.options.length + 1);
211
- expect($options.eq(index + 1).text()).toEqual(searchResult.options[index].text);
212
- expect($options.eq(index + 1).val()).toEqual(searchResult.options[index].value);
213
- });
214
-
215
- done();
216
- }, 200);
217
- });
218
- });
219
- });
1
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+
5
+ // It seems PhantomJS has issue with createComment and createRange
6
+ const isEnvSupportDocRange = ((document) => {
7
+ let docRange;
8
+ let commentNode;
9
+ const commentText = 'x';
10
+ let ret = true;
11
+
12
+ if (typeof document.createRange !== 'function') {
13
+ return (ret = false);
14
+ }
15
+ try {
16
+ docRange = document.createRange();
17
+ docRange.deleteContents();
18
+ commentNode = document.createComment(commentText);
19
+ if (commentNode.nodeType !== 8 || commentNode.textContent !== commentText) {
20
+ return (ret = false);
21
+ }
22
+ } catch {
23
+ return (ret = false);
24
+ }
25
+ return ret;
26
+ })(document);
27
+
28
+ describe('When search-results-component with forOf binding inited', () => {
29
+ const namespace: any = {};
30
+
31
+ beforeEach(() => {
32
+ loadFixture('test/fixtures/forOfBinding.html');
33
+
34
+ namespace.viewModel = {
35
+ searchResultTitle: 'Featured service providers',
36
+ messageTriggerCss: '',
37
+ bookmarkCss: '',
38
+ searchResults: [
39
+ {
40
+ id: '001',
41
+ title: 'Card title that wraps to a new line',
42
+ description:
43
+ 'This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.',
44
+ image: 'bootstrap/images/pic-home.jpg',
45
+ bookmarked: false,
46
+ numLikes: 110,
47
+ options: [{text: '1', value: '1'}, {text: '2', value: '2'}, {text: '3', value: '3'}],
48
+ },
49
+ {
50
+ id: '456',
51
+ title: 'Card title',
52
+ description: 'This card has supporting text below as a natural lead-in to additional content.',
53
+ image: '',
54
+ bookmarked: false,
55
+ numLikes: 8,
56
+ selected: true,
57
+ options: [{text: '4', value: '4'}, {text: '5', value: '5'}, {text: '6', value: '6'}],
58
+ },
59
+ {
60
+ id: '789',
61
+ title: 'Sample carpemter service',
62
+ description:
63
+ 'This is a wider card with supporting text below as a natural lead-in to additional content. This card has even longer content than the first to show that equal height action.',
64
+ image: 'bootstrap/images/pic-carpenter.jpg',
65
+ bookmarked: false,
66
+ numLikes: 8,
67
+ highlight: true,
68
+ highlightCss: 'result-item--highlight',
69
+ options: [{text: '7', value: '7'}, {text: '8', value: '8'}, {text: '9', value: '9'}],
70
+ },
71
+ ],
72
+ getResultItemAttr(index: number, _oldAttrObj: any, _$el: any) {
73
+ const self = this;
74
+ if (self.searchResults[index].image) {
75
+ return {
76
+ src: self.searchResults[index].image,
77
+ alt: self.searchResults[index].title || '',
78
+ };
79
+ }
80
+ },
81
+ setResultOptionAttr($data: any, _oldAttrObj: any, _$el: any) {
82
+ if ($data && $data.value) {
83
+ // todo: the index here is the outter loop index
84
+ return {
85
+ value: $data.value,
86
+ };
87
+ }
88
+ },
89
+ onAdMessageCheck(e: Event, $el: any, newValue: any, oldValue: any, index: number) {
90
+ console.log('onAdMessageCheck: ', $el, newValue, oldValue, index);
91
+ },
92
+ onAdBookmarkClick(e: Event, $el: any, index: number) {
93
+ e.preventDefault();
94
+ console.log('onAdBookmarkClick: ', $el, index);
95
+ },
96
+ };
97
+
98
+ namespace.searchResultsComponent = dataBind.init(
99
+ document.querySelector('[data-bind-comp="search-results-component"]'),
100
+ namespace.viewModel,
101
+ );
102
+
103
+ namespace.searchResultsComponent.render();
104
+ });
105
+
106
+ afterEach(() => {
107
+ // clean up all app/components
108
+ for (const prop in namespace) {
109
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
110
+ delete namespace[prop];
111
+ }
112
+ }
113
+ });
114
+
115
+ it('Then [data-bind-comp="search-results-component"] should have render', async () => {
116
+ // skip if test environment doesn't support document.createRange
117
+ if (!isEnvSupportDocRange) {
118
+ expect(isEnvSupportDocRange).toBe(false);
119
+ return;
120
+ }
121
+ await waitFor(() => {
122
+ expect(document.querySelector('#searchResultTitle')!.textContent).toBe(namespace.viewModel.searchResultTitle);
123
+ }, {timeout: 500});
124
+ });
125
+
126
+ it('Then render forOf binding elements with comment tag wrap around', async () => {
127
+ const $searchColumn = document.getElementById('search-result-columns');
128
+ // not sure why jasmine first execution before render complete, that's why element doesn't exist
129
+ // but when run just this spec it will works
130
+ if (!$searchColumn || !$searchColumn.firstElementChild) {
131
+ expect($searchColumn?.firstElementChild).toBeFalsy();
132
+ return;
133
+ }
134
+
135
+ await waitFor(() => {
136
+ const firstComment = $searchColumn.firstChild;
137
+ const lastComment = $searchColumn.lastChild;
138
+ expect(firstComment!.nodeType).toBe(Node.COMMENT_NODE);
139
+ expect(lastComment!.nodeType).toBe(Node.COMMENT_NODE);
140
+ }, {timeout: 500});
141
+ });
142
+
143
+ it('Then render same amount of items in viewModel.searchResults', async () => {
144
+ const $searchColumn = document.getElementById('search-result-columns');
145
+ // not sure why jasmine first execution before render complete, that's why element doesn't exist
146
+ // but when run just this spec it will works
147
+ if (!$searchColumn || !$searchColumn.firstElementChild) {
148
+ expect($searchColumn?.firstElementChild).toBeFalsy();
149
+ return;
150
+ }
151
+
152
+ await waitFor(() => {
153
+ const $results = Array.from($searchColumn.children);
154
+ expect($results.length).toBe(namespace.viewModel.searchResults.length);
155
+ }, {timeout: 500});
156
+ });
157
+
158
+ describe('When each search item rendered', () => {
159
+ it('should render bindings according to searchResults data', async () => {
160
+ const $searchColumn = document.querySelector('#search-result-columns');
161
+ const $results = $searchColumn ? Array.from($searchColumn.children) : [];
162
+
163
+ // not sure why jasmine first execution before render complete, that's why element doesn't exist
164
+ // but when run just this spec it will works
165
+ if (!$results.length) {
166
+ expect($results.length).toBe(0);
167
+ return;
168
+ }
169
+
170
+ await waitFor(() => {
171
+ expect($results.length).not.toBe(0);
172
+
173
+ $results.forEach(($result, index) => {
174
+ const indexString = String(index);
175
+ const $img = $result.querySelector('.result-item__img');
176
+ const $body = $result.querySelector('.card-body')!;
177
+ const $footer = $result.querySelector('.result-item__footer')!;
178
+ const $checkbox = $footer.querySelector('.result-item__icon-checkbox') as HTMLInputElement;
179
+ const $options = $footer.querySelectorAll('select.form-control option');
180
+ const imgSrc = $img ? $img.getAttribute('src') || '' : '';
181
+ let bodyIndex = $body.querySelector('.bodyIndex')?.textContent || '';
182
+ let footerIndex = $footer.querySelector('.footerIndex')?.textContent || '';
183
+ let bookMarkIndex = $result.querySelector('.bookMarkIndex')?.textContent || '';
184
+ const searchResult = namespace.viewModel.searchResults[index];
185
+
186
+ bodyIndex = bodyIndex.charAt(bodyIndex.length - 1);
187
+ footerIndex = footerIndex.charAt(footerIndex.length - 1);
188
+ bookMarkIndex = bookMarkIndex.charAt(bookMarkIndex.length - 1);
189
+
190
+ expect($img).not.toBeNull();
191
+ expect(imgSrc).toBe(namespace.viewModel.searchResults[index].image);
192
+ expect($body.children.length).not.toBe(0);
193
+ expect(bodyIndex).toEqual(indexString);
194
+ expect($footer).not.toBeNull();
195
+ expect(footerIndex).toEqual(indexString);
196
+ expect(bookMarkIndex).toEqual(indexString);
197
+ expect($checkbox.checked).toEqual(Boolean(searchResult.selected));
198
+ // first option is not from data
199
+ expect($options.length).toEqual(searchResult.options.length + 1);
200
+ if ($options[index + 1]) {
201
+ expect($options[index + 1].textContent).toEqual(searchResult.options[index].text);
202
+ expect(($options[index + 1] as HTMLOptionElement).value).toEqual(searchResult.options[index].value);
203
+ }
204
+ });
205
+ }, {timeout: 500});
206
+ });
207
+ });
208
+ });