jqtree 1.6.0 → 1.6.3

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 (301) hide show
  1. package/.eslintrc +3 -11
  2. package/.github/workflows/ci.yml +48 -23
  3. package/.github/workflows/codeql-analysis.yml +9 -5
  4. package/.prettier +3 -0
  5. package/README.md +11 -8
  6. package/bower.json +1 -1
  7. package/{.postcssrc → config/.postcssrc} +0 -0
  8. package/config/babel.config.json +11 -0
  9. package/config/babel.coverage.config.json +4 -0
  10. package/{jest-jsdom.config.js → config/jest.config.js} +9 -5
  11. package/config/playwright.config.js +18 -0
  12. package/config/production +4 -0
  13. package/{rollup.config.js → config/rollup.config.js} +19 -14
  14. package/{jqtree.postcss → css/jqtree.postcss} +3 -0
  15. package/devserver/index.html +1 -9
  16. package/docs/Gemfile +2 -0
  17. package/docs/Gemfile.lock +263 -0
  18. package/{_config.yml → docs/_config.yml} +8 -1
  19. package/{_entries → docs/_entries}/01_general.md +0 -0
  20. package/{_entries → docs/_entries}/02_introduction.md +0 -0
  21. package/{_entries → docs/_entries}/03_features.md +1 -1
  22. package/{_entries → docs/_entries}/04_demo.html +1 -7
  23. package/{_entries → docs/_entries}/05_requirements.md +0 -0
  24. package/{_entries → docs/_entries}/06_downloads.md +0 -0
  25. package/{_entries → docs/_entries}/07_tutorial.md +0 -0
  26. package/{_entries → docs/_entries}/08_examples.md +0 -0
  27. package/{_entries → docs/_entries}/09_usecases.md +0 -0
  28. package/{_entries → docs/_entries}/10_changelog.md +14 -0
  29. package/{_entries → docs/_entries}/11_options.md +0 -0
  30. package/{_entries → docs/_entries}/12_animationspeed.md +0 -0
  31. package/{_entries → docs/_entries}/13_autoescape.md +0 -0
  32. package/{_entries → docs/_entries}/14_autoopen.md +0 -0
  33. package/{_entries → docs/_entries}/15_buttonleft.md +0 -0
  34. package/{_entries → docs/_entries}/16_closedicon.md +0 -0
  35. package/{_entries → docs/_entries}/17_data.md +0 -0
  36. package/{_entries → docs/_entries}/18_datafilter.md +0 -0
  37. package/{_entries → docs/_entries}/19_data-url.md +0 -0
  38. package/{_entries → docs/_entries}/20_draganddrop.md +0 -0
  39. package/{_entries → docs/_entries}/21_keyboardsupport.md +0 -0
  40. package/{_entries → docs/_entries}/22_oncanmove.md +0 -0
  41. package/{_entries → docs/_entries}/23_oncanmoveto.md +2 -2
  42. package/{_entries → docs/_entries}/24_oncanselectnode.md +0 -0
  43. package/{_entries → docs/_entries}/25_oncreateli.md +2 -2
  44. package/{_entries → docs/_entries}/26_ondragmove.md +0 -0
  45. package/{_entries → docs/_entries}/27_ondragstop.md +0 -0
  46. package/{_entries → docs/_entries}/28_onismovehandle.md +0 -0
  47. package/{_entries → docs/_entries}/29_onloadfailed.md +0 -0
  48. package/{_entries → docs/_entries}/30_onloading.md +2 -2
  49. package/{_entries → docs/_entries}/31_openedicon.md +0 -0
  50. package/{_entries → docs/_entries}/32_openfolderdelay.md +2 -0
  51. package/{_entries → docs/_entries}/33_rtl.md +0 -0
  52. package/{_entries → docs/_entries}/34_savestate.md +0 -0
  53. package/{_entries → docs/_entries}/35_selectable.md +0 -0
  54. package/{_entries → docs/_entries}/36_showemptyfolder.md +0 -0
  55. package/{_entries → docs/_entries}/37_slide.md +0 -0
  56. package/{_entries → docs/_entries}/38_start_dnd_delay.md +0 -0
  57. package/{_entries → docs/_entries}/39_tabindex.md +0 -0
  58. package/{_entries → docs/_entries}/40_usecontextmenu.md +0 -0
  59. package/{_entries → docs/_entries}/41_functions.md +0 -0
  60. package/{_entries → docs/_entries}/42_addparentnode.md +1 -1
  61. package/{_entries → docs/_entries}/43_addnodeafter.md +1 -1
  62. package/{_entries → docs/_entries}/44_addnodebefore.md +1 -1
  63. package/{_entries → docs/_entries}/45_appendnode.md +5 -5
  64. package/{_entries → docs/_entries}/46_closenode.md +0 -0
  65. package/{_entries → docs/_entries}/47_destroy.md +0 -0
  66. package/{_entries → docs/_entries}/48_getnodebycallback.md +0 -0
  67. package/{_entries → docs/_entries}/49_getnodebyid.md +0 -0
  68. package/{_entries → docs/_entries}/50_getnodebyhtmlelement.md +2 -2
  69. package/{_entries → docs/_entries}/51_getselectednode.md +0 -0
  70. package/{_entries → docs/_entries}/52_getstate.md +0 -0
  71. package/{_entries → docs/_entries}/53_gettree.md +1 -1
  72. package/{_entries → docs/_entries}/54_isdragging.md +1 -1
  73. package/{_entries → docs/_entries}/55_loaddata.md +3 -3
  74. package/{_entries → docs/_entries}/56_loaddatafromurl.md +4 -4
  75. package/{_entries → docs/_entries}/57_movedown.md +0 -0
  76. package/{_entries → docs/_entries}/58_movenode.md +3 -3
  77. package/{_entries → docs/_entries}/59_moveup.md +0 -0
  78. package/{_entries → docs/_entries}/60_opennode.md +3 -3
  79. package/docs/_entries/61_prependnode.md +21 -0
  80. package/docs/_entries/62_refresh.md +12 -0
  81. package/{_entries/62_reload.md → docs/_entries/63_reload.md} +2 -2
  82. package/{_entries/63_removenode.md → docs/_entries/64_removenode.md} +0 -0
  83. package/{_entries/64_selectnode.md → docs/_entries/65_selectnode.md} +0 -0
  84. package/{_entries/65_scrolltonode.md → docs/_entries/66_scrolltonode.md} +0 -0
  85. package/{_entries/66_setoption.md → docs/_entries/67_setoption.md} +0 -0
  86. package/{_entries/67_setstate.md → docs/_entries/68_setstate.md} +0 -0
  87. package/{_entries/68_toggle.md → docs/_entries/69_toggle.md} +0 -0
  88. package/{_entries/69_tojson.md → docs/_entries/70_tojson.md} +0 -0
  89. package/{_entries/70_updatenode.md → docs/_entries/71_updatenode.md} +1 -1
  90. package/{_entries/71_events.md → docs/_entries/72_events.md} +0 -0
  91. package/{_entries/72_tree-click.md → docs/_entries/73_tree-click.md} +0 -0
  92. package/{_entries/73_tree-close.md → docs/_entries/74_tree-close.md} +0 -0
  93. package/{_entries/74_tree-contextmenu.md → docs/_entries/75_tree-contextmenu.md} +0 -0
  94. package/{_entries/75_tree-dblclick.md → docs/_entries/76_tree-dblclick.md} +0 -0
  95. package/{_entries/76_tree-init.md → docs/_entries/77_tree-init.md} +0 -0
  96. package/{_entries/77_tree-load-data.md → docs/_entries/78_tree-load-data.md} +0 -0
  97. package/{_entries/78_tree-loading-data.md → docs/_entries/79_tree-loading-data.md} +0 -0
  98. package/{_entries/79_tree-move.md → docs/_entries/80_tree-move.md} +0 -0
  99. package/{_entries/80_tree-refresh.md → docs/_entries/81_tree-refresh.md} +0 -0
  100. package/{_entries/81_tree-open.md → docs/_entries/82_tree-open.md} +0 -0
  101. package/{_entries/82_tree-select.md → docs/_entries/83_tree-select.md} +0 -0
  102. package/{_entries/83_multiple-selection.md → docs/_entries/84_multiple-selection.md} +0 -0
  103. package/{_entries/84_add-to-selection.md → docs/_entries/85_add-to-selection.md} +0 -0
  104. package/{_entries/85_get-selected-nodes.md → docs/_entries/86_get-selected-nodes.md} +0 -0
  105. package/{_entries/86_is-node-selected.md → docs/_entries/87_is-node-selected.md} +1 -1
  106. package/{_entries/87_remove-from-selection.md → docs/_entries/88_remove-from-selection.md} +0 -0
  107. package/{_entries/88_node-functions.md → docs/_entries/89_node-functions.md} +0 -0
  108. package/{_entries/89_children.md → docs/_entries/90_children.md} +0 -0
  109. package/{_entries/90_getdata.md → docs/_entries/91_getdata.md} +2 -2
  110. package/{_entries/91_getlevel.md → docs/_entries/92_getlevel.md} +0 -0
  111. package/{_entries/92_getnextnode.md → docs/_entries/93_getnextnode.md} +0 -0
  112. package/{_entries/93_getnextsibling.md → docs/_entries/94_getnextsibling.md} +0 -0
  113. package/{_entries/94_getpreviousnode.md → docs/_entries/95_getpreviousnode.md} +0 -0
  114. package/{_entries/95_getprevioussibling.md → docs/_entries/96_getprevioussibling.md} +0 -0
  115. package/{_entries/96_parent.md → docs/_entries/97_parent.md} +1 -1
  116. package/{_entries → docs/_entries}/insert.py +0 -0
  117. package/{_entries → docs/_entries}/renumber.py +0 -0
  118. package/{_examples → docs/_examples}/01_load_json_data.html +3 -5
  119. package/{_examples → docs/_examples}/02_load_json_data_from_server.html +3 -5
  120. package/{_examples → docs/_examples}/03_drag_and_drop.html +3 -5
  121. package/{_examples → docs/_examples}/04_save_state.html +3 -5
  122. package/{_examples → docs/_examples}/05_load_on_demand.html +3 -5
  123. package/{_examples → docs/_examples}/06_autoescape.html +3 -5
  124. package/{_examples → docs/_examples}/07_autoscroll.html +3 -5
  125. package/{_examples → docs/_examples}/08_multiple_select.html +3 -5
  126. package/{_examples → docs/_examples}/09_custom_html.html +3 -5
  127. package/{_examples → docs/_examples}/10_icon_buttons.html +3 -5
  128. package/{_examples → docs/_examples}/11_right-to-left.html +3 -5
  129. package/{_examples → docs/_examples}/12_button_on_right.html +3 -5
  130. package/docs/_examples/13_drag_outside.html +48 -0
  131. package/docs/_examples/14_filter.html +111 -0
  132. package/docs/_layouts/example.html +7 -0
  133. package/docs/_layouts/page.html +26 -0
  134. package/docs/documentation.css +3 -0
  135. package/docs/index.html +65 -0
  136. package/docs/jqtree.css +189 -0
  137. package/docs/package.json +22 -0
  138. package/docs/pnpm-lock.yaml +768 -0
  139. package/docs/postcss.config.js +7 -0
  140. package/docs/static/bower.json +8 -0
  141. package/docs/static/bower_components/fontawesome/css/all.min.css +5 -0
  142. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-brands-400.eot +0 -0
  143. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-brands-400.svg +774 -627
  144. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-brands-400.ttf +0 -0
  145. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.woff +0 -0
  146. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
  147. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-regular-400.eot +0 -0
  148. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-regular-400.svg +93 -95
  149. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-regular-400.ttf +0 -0
  150. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.woff +0 -0
  151. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
  152. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-solid-900.eot +0 -0
  153. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-solid-900.svg +1164 -1074
  154. package/{static → docs/static}/bower_components/fontawesome/webfonts/fa-solid-900.ttf +0 -0
  155. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.woff +0 -0
  156. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
  157. package/{static → docs/static}/bower_components/jquery/dist/jquery.js +118 -109
  158. package/docs/static/bower_components/jquery/dist/jquery.min.js +2 -0
  159. package/{static → docs/static}/bower_components/jquery-mockjax/dist/jquery.mockjax.js +15 -9
  160. package/docs/static/documentation.css +1313 -0
  161. package/docs/static/documentation.js +26 -0
  162. package/{static → docs/static}/example.css +14 -33
  163. package/{static → docs/static}/example.postcss +15 -32
  164. package/{static → docs/static}/example_data.js +33 -36
  165. package/{static → docs/static}/examples/autoescape.js +0 -0
  166. package/{static → docs/static}/examples/autoscroll.js +0 -0
  167. package/{static → docs/static}/examples/button-on-right.js +0 -0
  168. package/{static → docs/static}/examples/custom_html.js +5 -9
  169. package/{static → docs/static}/examples/drag-outside.js +0 -0
  170. package/{static → docs/static}/examples/drag_and_drop.js +0 -0
  171. package/docs/static/examples/filter.js +63 -0
  172. package/{static → docs/static}/examples/icon_buttons.js +0 -0
  173. package/{static → docs/static}/examples/load_json_data.js +0 -0
  174. package/{static → docs/static}/examples/load_json_data_from_server.js +0 -0
  175. package/{static → docs/static}/examples/load_on_demand.js +0 -0
  176. package/{static → docs/static}/examples/multiple_select.js +0 -0
  177. package/{static → docs/static}/examples/right-to-left.js +0 -0
  178. package/{static → docs/static}/examples/save_state.js +0 -0
  179. package/{static → docs/static}/monokai.css +0 -0
  180. package/{static → docs/static}/spinner.gif +0 -0
  181. package/docs/tailwind.config.js +16 -0
  182. package/docs/tree.jquery.js +21 -0
  183. package/jqtree.css +4 -1
  184. package/lib/dataLoader.js +146 -98
  185. package/lib/dragAndDropHandler.js +672 -470
  186. package/lib/elementsRenderer.js +282 -197
  187. package/lib/jqtreeOptions.js +1 -2
  188. package/lib/keyHandler.js +134 -87
  189. package/lib/mouse.widget.js +285 -184
  190. package/lib/node.js +691 -505
  191. package/lib/nodeElement.js +329 -205
  192. package/lib/playwright/coverage.js +140 -0
  193. package/lib/playwright/playwright.test.js +298 -179
  194. package/lib/playwright/testUtils.js +267 -0
  195. package/lib/saveStateHandler.js +311 -204
  196. package/lib/scrollHandler.js +293 -199
  197. package/lib/selectNodeHandler.js +140 -100
  198. package/lib/simple.widget.js +184 -109
  199. package/lib/test/global.d.js +3 -0
  200. package/lib/test/jqTree/create.test.js +44 -40
  201. package/lib/test/jqTree/events.test.js +186 -138
  202. package/lib/test/jqTree/keyboard.test.js +216 -199
  203. package/lib/test/jqTree/loadOnDemand.test.js +238 -157
  204. package/lib/test/jqTree/methods.test.js +1289 -1019
  205. package/lib/test/jqTree/options.test.js +491 -410
  206. package/lib/test/node.test.js +1036 -873
  207. package/lib/test/nodeUtil.test.js +21 -20
  208. package/lib/test/support/exampleData.js +35 -23
  209. package/lib/test/support/jqTreeMatchers.js +72 -54
  210. package/lib/test/support/matchers.d.js +1 -0
  211. package/lib/test/support/setupTests.js +9 -3
  212. package/lib/test/support/testUtil.js +35 -15
  213. package/lib/test/support/treeStructure.js +41 -32
  214. package/lib/test/util.test.js +21 -20
  215. package/lib/tree.jquery.d.js +1 -0
  216. package/lib/tree.jquery.js +1264 -886
  217. package/lib/types.js +1 -2
  218. package/lib/typings.d.js +2 -0
  219. package/lib/util.js +19 -8
  220. package/lib/version.js +8 -3
  221. package/package.json +54 -47
  222. package/src/dataLoader.ts +6 -6
  223. package/src/dragAndDropHandler.ts +8 -8
  224. package/src/elementsRenderer.ts +4 -0
  225. package/src/jqtreeOptions.ts +2 -2
  226. package/src/mouse.widget.ts +19 -15
  227. package/src/node.ts +27 -41
  228. package/src/nodeElement.ts +17 -9
  229. package/src/playwright/.eslintrc +5 -0
  230. package/src/playwright/coverage.ts +41 -0
  231. package/src/playwright/playwright.test.ts +75 -77
  232. package/src/playwright/playwright.test.ts-snapshots/with-dragAndDrop-moves-a-node-1-Chromium-darwin.png +0 -0
  233. package/src/playwright/playwright.test.ts-snapshots/with-dragAndDrop-moves-a-node-1-Chromium-linux.png +0 -0
  234. package/src/playwright/playwright.test.ts-snapshots/without-dragAndDrop-displays-a-tree-1-Chromium-darwin.png +0 -0
  235. package/src/playwright/playwright.test.ts-snapshots/without-dragAndDrop-displays-a-tree-1-Chromium-linux.png +0 -0
  236. package/src/playwright/playwright.test.ts-snapshots/without-dragAndDrop-selects-a-node-1-Chromium-darwin.png +0 -0
  237. package/src/playwright/playwright.test.ts-snapshots/without-dragAndDrop-selects-a-node-1-Chromium-linux.png +0 -0
  238. package/src/playwright/testUtils.ts +122 -0
  239. package/src/saveStateHandler.ts +11 -6
  240. package/src/selectNodeHandler.ts +1 -1
  241. package/src/simple.widget.ts +1 -1
  242. package/src/test/.eslintrc +14 -0
  243. package/src/test/jqTree/create.test.ts +0 -1
  244. package/src/test/jqTree/events.test.ts +10 -10
  245. package/src/test/jqTree/keyboard.test.ts +0 -1
  246. package/src/test/jqTree/loadOnDemand.test.ts +56 -11
  247. package/src/test/jqTree/methods.test.ts +72 -55
  248. package/src/test/jqTree/options.test.ts +30 -33
  249. package/src/test/node.test.ts +2 -2
  250. package/src/test/support/jqTreeMatchers.ts +8 -9
  251. package/src/test/support/matchers.d.ts +2 -4
  252. package/src/test/support/setupTests.ts +2 -1
  253. package/src/tree.jquery.d.ts +19 -13
  254. package/src/tree.jquery.ts +35 -28
  255. package/src/version.ts +1 -1
  256. package/tree.jquery.debug.js +4810 -3325
  257. package/tree.jquery.debug.js.map +1 -1
  258. package/tree.jquery.js +3 -3
  259. package/tree.jquery.js.map +1 -1
  260. package/tsconfig.json +1 -0
  261. package/_entries/61_prependnode.md +0 -21
  262. package/_examples/13_drag_outside.html +0 -25
  263. package/_layouts/base.html +0 -55
  264. package/_layouts/frontpage.html +0 -20
  265. package/_layouts/page.html +0 -7
  266. package/index.html +0 -48
  267. package/jest-browser.config.js +0 -18
  268. package/jest-playwright.config.js +0 -21
  269. package/jest.config.js +0 -8
  270. package/lib/playwright/testUtil.js +0 -223
  271. package/lib/playwright/visualRegression.js +0 -128
  272. package/production +0 -5
  273. package/src/playwright/screenshots/displays_a_tree_Desktop.png +0 -0
  274. package/src/playwright/screenshots/displays_a_tree_iPhone 6.png +0 -0
  275. package/src/playwright/screenshots/moves_a_node_Desktop.png +0 -0
  276. package/src/playwright/screenshots/moves_a_node_iPhone 6.png +0 -0
  277. package/src/playwright/screenshots/opens_a_node_Desktop.png +0 -0
  278. package/src/playwright/screenshots/opens_a_node_iPhone 6.png +0 -0
  279. package/src/playwright/screenshots/selects_a_node_Desktop.png +0 -0
  280. package/src/playwright/screenshots/selects_a_node_iPhone 6.png +0 -0
  281. package/src/playwright/testUtil.ts +0 -171
  282. package/src/playwright/visualRegression.ts +0 -88
  283. package/static/bower.json +0 -9
  284. package/static/bower_components/bootstrap/dist/css/bootstrap-theme.min.css +0 -6
  285. package/static/bower_components/bootstrap/dist/css/bootstrap.min.css +0 -6
  286. package/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  287. package/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg +0 -288
  288. package/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  289. package/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  290. package/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  291. package/static/bower_components/bootstrap/dist/js/bootstrap.min.js +0 -6
  292. package/static/bower_components/fontawesome/css/all.min.css +0 -5
  293. package/static/bower_components/fontawesome/webfonts/fa-brands-400.woff +0 -0
  294. package/static/bower_components/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
  295. package/static/bower_components/fontawesome/webfonts/fa-regular-400.woff +0 -0
  296. package/static/bower_components/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
  297. package/static/bower_components/fontawesome/webfonts/fa-solid-900.woff +0 -0
  298. package/static/bower_components/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
  299. package/static/bower_components/jquery/dist/jquery.min.js +0 -2
  300. package/static/documentation.css +0 -171
  301. package/static/documentation.js +0 -48
@@ -1,37 +1,27 @@
1
- /// <reference types="jest-playwright-preset" />
2
- /// <reference types="expect-playwright" />
3
-
4
- import getGiven from "givens";
1
+ import { test, expect, Page } from "@playwright/test";
5
2
  import {
6
3
  dragAndDrop,
7
- expectToBeClosed,
8
- expectToBeOpen,
9
- expectToBeSelected,
10
4
  findNodeElement,
11
5
  getTreeStructure,
12
- openNode,
13
6
  selectNode,
14
- } from "./testUtil";
15
- import { matchScreenshot } from "./visualRegression";
7
+ } from "./testUtils";
8
+ import { initCoverage, saveCoverage } from "./coverage";
16
9
 
17
- interface Vars {
10
+ interface InitPageParameters {
11
+ baseURL?: string;
18
12
  dragAndDrop: boolean;
13
+ page: Page;
19
14
  }
20
15
 
21
- const given = getGiven<Vars>();
22
- given("dragAndDrop", () => false);
23
-
24
- beforeEach(async () => {
25
- await jestPlaywright.resetPage();
16
+ const initPage = async ({ baseURL, dragAndDrop, page }: InitPageParameters) => {
17
+ if (!baseURL) {
18
+ throw new Error("Missing baseURL");
19
+ }
26
20
 
27
- await page.goto("http://localhost:8080/test_index.html");
21
+ await page.goto(`${baseURL}/test_index.html`);
28
22
  await page.waitForLoadState("domcontentloaded");
29
23
 
30
- // Fix error on iphone6 device when collecting coverage
31
- await page.evaluate(() => {
32
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
33
- (window as any)["reportCodeCoverage"] = () => null;
34
- });
24
+ page.on("console", (msg) => console.log(`console: ${msg.text()}`));
35
25
 
36
26
  await page.evaluate(`
37
27
  const $tree = jQuery("#tree1");
@@ -40,75 +30,83 @@ beforeEach(async () => {
40
30
  animationSpeed: 0,
41
31
  autoOpen: 0,
42
32
  data: ExampleData.exampleData,
43
- dragAndDrop: ${given.dragAndDrop},
33
+ dragAndDrop: ${dragAndDrop},
44
34
  startDndDelay: 100,
45
35
  });
46
36
  `);
47
- });
48
37
 
49
- afterEach(async () => {
50
- await jestPlaywright.saveCoverage(page);
51
- });
38
+ await page.evaluate(`
39
+ console.log(window.__coverage__ ? 'Coverage enabled' : 'Coverage not enabled');
40
+ `);
41
+ };
52
42
 
53
- it("displays a tree", async () => {
54
- await expect(page).toHaveText("Saurischia");
55
- await expect(page).toHaveText("Ornithischians");
56
- await expect(page).toHaveText("Coelophysoids");
43
+ test.beforeEach(async ({ context }) => {
44
+ await initCoverage(context);
45
+ });
57
46
 
58
- await matchScreenshot("displays_a_tree");
47
+ test.afterEach(async ({ context }) => {
48
+ await saveCoverage(context);
59
49
  });
60
50
 
61
- it("selects a node", async () => {
62
- await expect(page).toHaveText("Saurischia");
63
- const saurischia = await findNodeElement("Saurischia");
64
- await selectNode(saurischia);
65
- await expectToBeSelected(saurischia);
51
+ test.describe("without dragAndDrop", () => {
52
+ test.beforeEach(async ({ baseURL, page }) => {
53
+ await initPage({ baseURL, page, dragAndDrop: false });
54
+ });
66
55
 
67
- await matchScreenshot("selects_a_node");
68
- });
56
+ test("displays a tree", async ({ page }) => {
57
+ await expect(page.locator("body")).toHaveText(/.*Saurischia.*/);
58
+ await expect(page.locator("body")).toHaveText(/.*Ornithischians.*/);
59
+ await expect(page.locator("body")).toHaveText(/.*Coelophysoids.*/);
60
+
61
+ const screenshot = await page.screenshot();
62
+ expect(screenshot).toMatchSnapshot();
63
+ });
69
64
 
70
- it("opens a node", async () => {
71
- await expect(page).toHaveText("Saurischia");
65
+ test("selects a node", async ({ page }) => {
66
+ await expect(page.locator("body")).toHaveText(/.*Saurischia.*/);
72
67
 
73
- const theropods = await findNodeElement("Theropods");
74
- await expectToBeClosed(theropods);
75
- await openNode(theropods);
76
- await expectToBeOpen(theropods);
68
+ const saurischia = await findNodeElement(page, "Saurischia");
69
+ await selectNode(saurischia);
77
70
 
78
- await matchScreenshot("opens_a_node");
71
+ const screenshot = await page.screenshot();
72
+ expect(screenshot).toMatchSnapshot();
73
+ });
79
74
  });
80
75
 
81
- describe("dragAndDrop", () => {
82
- given("dragAndDrop", () => true);
83
-
84
- it("moves a node", async () => {
85
- await dragAndDrop("Herrerasaurians", "Ornithischians");
86
-
87
- await getTreeStructure().then((structure) => {
88
- expect(structure).toEqual([
89
- expect.objectContaining({
90
- name: "Saurischia",
91
- children: [
92
- expect.objectContaining({ name: "Theropods" }),
93
- expect.objectContaining({ name: "Sauropodomorphs" }),
94
- ],
95
- }),
96
- expect.objectContaining({
97
- name: "Ornithischians",
98
- children: [
99
- expect.objectContaining({ name: "Herrerasaurians" }),
100
- expect.objectContaining({ name: "Heterodontosaurids" }),
101
- expect.objectContaining({ name: "Thyreophorans" }),
102
- expect.objectContaining({ name: "Ornithopods" }),
103
- expect.objectContaining({
104
- name: "Pachycephalosaurians",
105
- }),
106
- expect.objectContaining({ name: "Ceratopsians" }),
107
- ],
108
- }),
109
- ]);
110
- });
76
+ test.describe("with dragAndDrop", () => {
77
+ test.beforeEach(async ({ baseURL, page }) => {
78
+ await initPage({ baseURL, page, dragAndDrop: true });
79
+ });
111
80
 
112
- await matchScreenshot("moves_a_node");
81
+ test("moves a node", async ({ page }) => {
82
+ await dragAndDrop(page, "Herrerasaurians", "Ornithischians");
83
+
84
+ const structure = await getTreeStructure(page);
85
+
86
+ expect(structure).toEqual([
87
+ expect.objectContaining({
88
+ name: "Saurischia",
89
+ children: [
90
+ expect.objectContaining({ name: "Theropods" }),
91
+ expect.objectContaining({ name: "Sauropodomorphs" }),
92
+ ],
93
+ }),
94
+ expect.objectContaining({
95
+ name: "Ornithischians",
96
+ children: [
97
+ expect.objectContaining({ name: "Herrerasaurians" }),
98
+ expect.objectContaining({ name: "Heterodontosaurids" }),
99
+ expect.objectContaining({ name: "Thyreophorans" }),
100
+ expect.objectContaining({ name: "Ornithopods" }),
101
+ expect.objectContaining({
102
+ name: "Pachycephalosaurians",
103
+ }),
104
+ expect.objectContaining({ name: "Ceratopsians" }),
105
+ ],
106
+ }),
107
+ ]);
108
+
109
+ const screenshot = await page.screenshot();
110
+ expect(screenshot).toMatchSnapshot();
113
111
  });
114
112
  });
@@ -0,0 +1,122 @@
1
+ import { Page, ElementHandle } from "@playwright/test";
2
+
3
+ const locateTitle = (page: Page, title: string) =>
4
+ page.locator(".jqtree-title", {
5
+ hasText: title,
6
+ });
7
+
8
+ export const findNodeElement = async (page: Page, title: string) => {
9
+ const titleElement = await locateTitle(page, title).elementHandle();
10
+
11
+ if (!titleElement) {
12
+ throw new Error(`Title element not found: ${title}`);
13
+ }
14
+
15
+ const nodeElement = await titleElement.evaluateHandle((el) => {
16
+ const li = el.closest("li");
17
+
18
+ if (!li) {
19
+ throw Error("Node element not found");
20
+ }
21
+
22
+ return li;
23
+ });
24
+
25
+ return nodeElement;
26
+ };
27
+
28
+ export const selectNode = async (nodeElement: ElementHandle) => {
29
+ const titleHandle = await nodeElement.$(".jqtree-title");
30
+
31
+ if (!titleHandle) {
32
+ throw new Error("Could not select: title element not found");
33
+ }
34
+
35
+ await titleHandle.click();
36
+ };
37
+
38
+ const getRect = async (elementHandle: ElementHandle<HTMLElement>) => {
39
+ const boundingBox = await elementHandle.boundingBox();
40
+
41
+ if (!boundingBox) {
42
+ throw "No bounding box";
43
+ }
44
+
45
+ return boundingBox;
46
+ };
47
+
48
+ export const getTreeStructure = async (page: Page) => {
49
+ const structure = await page.evaluate<string>(`
50
+ ;
51
+ function getTreeNode($li) {
52
+ const $div = $li.children("div.jqtree-element");
53
+ const $span = $div.children("span.jqtree-title");
54
+ const name = $span.text();
55
+ const selected = $li.hasClass("jqtree-selected");
56
+
57
+ if ($li.hasClass("jqtree-folder")) {
58
+ const $ul = $li.children("ul.jqtree_common");
59
+
60
+ return {
61
+ nodeType: "folder",
62
+ children: getChildren($ul),
63
+ name,
64
+ open: !$li.hasClass("jqtree-closed"),
65
+ selected,
66
+ };
67
+ } else {
68
+ return {
69
+ nodeType: "child",
70
+ name,
71
+ selected,
72
+ };
73
+ }
74
+ }
75
+
76
+ function getChildren($ul) {
77
+ return $ul
78
+ .children("li.jqtree_common")
79
+ .map((_, li) => {
80
+ return getTreeNode(jQuery(li));
81
+ })
82
+ .get();
83
+ }
84
+
85
+ JSON.stringify(window.getChildren(jQuery("ul.jqtree-tree")));
86
+ `);
87
+
88
+ return JSON.parse(structure) as JQTreeMatchers.TreeStructure;
89
+ };
90
+
91
+ const getNodeRect = async (page: Page, title: string) => {
92
+ const titleElement = await locateTitle(page, title).elementHandle();
93
+
94
+ if (!titleElement) {
95
+ throw Error("Element not found");
96
+ }
97
+
98
+ const rect = await getRect(titleElement as ElementHandle<HTMLElement>);
99
+ return rect;
100
+ };
101
+
102
+ export const dragAndDrop = async (
103
+ page: Page,
104
+ from: string,
105
+ to: string
106
+ ): Promise<void> => {
107
+ const fromRect = await getNodeRect(page, from);
108
+ const toRect = await getNodeRect(page, to);
109
+
110
+ await page.mouse.move(
111
+ fromRect.x + fromRect.width / 2,
112
+ fromRect.y + fromRect.height / 2
113
+ );
114
+ await page.mouse.down();
115
+ // eslint-disable-next-line playwright/no-wait-for-timeout
116
+ await page.waitForTimeout(200);
117
+ await page.mouse.move(
118
+ toRect.x + toRect.width / 2,
119
+ toRect.y + toRect.height / 2
120
+ );
121
+ await page.mouse.up();
122
+ };
@@ -1,6 +1,11 @@
1
1
  import { isInt } from "./util";
2
2
  import { JqTreeWidget } from "./tree.jquery";
3
- import { Node, NodeId } from "./node";
3
+ import { Node } from "./node";
4
+
5
+ export interface SavedState {
6
+ open_nodes: NodeId[];
7
+ selected_node: NodeId[];
8
+ }
4
9
 
5
10
  export default class SaveStateHandler {
6
11
  private treeWidget: JqTreeWidget;
@@ -24,7 +29,7 @@ export default class SaveStateHandler {
24
29
  const jsonData = this.loadFromStorage();
25
30
 
26
31
  if (jsonData) {
27
- return (this.parseState(jsonData) as unknown) as SavedState;
32
+ return this.parseState(jsonData) as unknown as SavedState;
28
33
  } else {
29
34
  return null;
30
35
  }
@@ -121,7 +126,7 @@ export default class SaveStateHandler {
121
126
  state.selected_node = [state.selected_node];
122
127
  }
123
128
 
124
- return (state as unknown) as SavedState;
129
+ return state as unknown as SavedState;
125
130
  }
126
131
 
127
132
  private loadFromStorage(): string | null {
@@ -134,11 +139,11 @@ export default class SaveStateHandler {
134
139
  }
135
140
  }
136
141
 
137
- private openInitialNodes(nodeIds: any[]): boolean {
142
+ private openInitialNodes(nodeIds: NodeId[]): boolean {
138
143
  let mustLoadOnDemand = false;
139
144
 
140
- for (const nodeDd of nodeIds) {
141
- const node = this.treeWidget.getNodeById(nodeDd);
145
+ for (const nodeId of nodeIds) {
146
+ const node = this.treeWidget.getNodeById(nodeId);
142
147
 
143
148
  if (node) {
144
149
  if (!node.load_on_demand) {
@@ -1,4 +1,4 @@
1
- import { Node, NodeId } from "./node";
1
+ import { Node } from "./node";
2
2
  import { JqTreeWidget } from "./tree.jquery";
3
3
 
4
4
  export default class SelectNodeHandler {
@@ -109,7 +109,7 @@ export default class SimpleWidget<WidgetOptions> {
109
109
 
110
110
  public options: WidgetOptions;
111
111
 
112
- protected $el: JQuery<HTMLElement>;
112
+ public $el: JQuery<HTMLElement>;
113
113
 
114
114
  constructor(el: HTMLElement, options: WidgetOptions) {
115
115
  this.$el = jQuery(el);
@@ -0,0 +1,14 @@
1
+ {
2
+ "plugins": ["jest", "testing-library"],
3
+ "extends": ["plugin:jest/all", "plugin:testing-library/dom"],
4
+ "rules": {
5
+ "jest/consistent-test-it": "off",
6
+ "jest/prefer-expect-assertions": "off",
7
+ "jest/no-duplicate-hooks": "off",
8
+ "jest/no-hooks": "off",
9
+ "jest/no-identical-title": "off",
10
+ "jest/prefer-strict-equal": "off",
11
+ "jest/require-hook": "off",
12
+ "jest/require-top-level-describe": "off"
13
+ }
14
+ }
@@ -1,4 +1,3 @@
1
- import * as $ from "jquery";
2
1
  import getGiven from "givens";
3
2
  import "../../tree.jquery";
4
3
  import exampleData from "../support/exampleData";
@@ -1,4 +1,3 @@
1
- import * as $ from "jquery";
2
1
  import getGiven from "givens";
3
2
  import { rest } from "msw";
4
3
  import { setupServer } from "msw/node";
@@ -8,16 +7,24 @@ import { titleSpan } from "../support/testUtil";
8
7
 
9
8
  const context = describe;
10
9
 
10
+ const server = setupServer();
11
+
12
+ beforeAll(() => server.listen());
13
+
11
14
  beforeEach(() => {
12
15
  $("body").append('<div id="tree1"></div>');
13
16
  });
14
17
 
15
18
  afterEach(() => {
19
+ server.resetHandlers();
20
+
16
21
  const $tree = $("#tree1");
17
22
  $tree.tree("destroy");
18
23
  $tree.remove();
19
24
  });
20
25
 
26
+ afterAll(() => server.close());
27
+
21
28
  describe("tree.click", () => {
22
29
  interface Vars {
23
30
  node1: INode;
@@ -132,19 +139,12 @@ describe("tree.init", () => {
132
139
  });
133
140
 
134
141
  context("with data loaded from an url", () => {
135
- let server: ReturnType<typeof setupServer> | null = null;
136
-
137
- beforeAll(() => {
138
- server = setupServer(
142
+ beforeEach(() => {
143
+ server.use(
139
144
  rest.get("/tree/", (_request, response, ctx) =>
140
145
  response(ctx.status(200), ctx.json(exampleData))
141
146
  )
142
147
  );
143
- server.listen();
144
- });
145
-
146
- afterAll(() => {
147
- server?.close();
148
148
  });
149
149
 
150
150
  it("is called", () =>
@@ -1,4 +1,3 @@
1
- import * as $ from "jquery";
2
1
  import getGiven from "givens";
3
2
  import "../../tree.jquery";
4
3
  import exampleData from "../support/exampleData";
@@ -1,4 +1,3 @@
1
- import * as $ from "jquery";
2
1
  import getGiven from "givens";
3
2
  import { screen } from "@testing-library/dom";
4
3
  import { rest } from "msw";
@@ -8,17 +7,25 @@ import { togglerLink } from "../support/testUtil";
8
7
 
9
8
  const context = describe;
10
9
 
10
+ const server = setupServer();
11
+
12
+ beforeAll(() => server.listen());
13
+
11
14
  beforeEach(() => {
12
15
  $("body").append('<div id="tree1"></div>');
13
16
  });
14
17
 
15
18
  afterEach(() => {
19
+ server.resetHandlers();
20
+
16
21
  const $tree = $("#tree1");
17
22
  $tree.tree("destroy");
18
23
  $tree.remove();
19
24
  localStorage.clear();
20
25
  });
21
26
 
27
+ afterAll(() => server.close());
28
+
22
29
  context("when a node has load_on_demand in the data", () => {
23
30
  interface Vars {
24
31
  autoOpen: boolean;
@@ -38,10 +45,8 @@ context("when a node has load_on_demand in the data", () => {
38
45
  },
39
46
  ];
40
47
 
41
- let server: ReturnType<typeof setupServer> | null = null;
42
-
43
- beforeAll(() => {
44
- server = setupServer(
48
+ beforeEach(() => {
49
+ server.use(
45
50
  rest.get("/tree/", (request, response, ctx) => {
46
51
  const parentId = request.url.searchParams.get("node");
47
52
 
@@ -55,12 +60,6 @@ context("when a node has load_on_demand in the data", () => {
55
60
  }
56
61
  })
57
62
  );
58
-
59
- server.listen();
60
- });
61
-
62
- afterAll(() => {
63
- server?.close();
64
63
  });
65
64
 
66
65
  beforeEach(() => {
@@ -106,6 +105,52 @@ context("when a node has load_on_demand in the data", () => {
106
105
  }),
107
106
  ]);
108
107
  });
108
+
109
+ context("when the node is selected and has the focus", () => {
110
+ beforeEach(() => {
111
+ given.$tree.tree("selectNode", given.node);
112
+ });
113
+
114
+ it("keeps the node selected and focused", async () => {
115
+ expect(given.node.element).toBeSelected();
116
+ expect(given.node.element).toBeFocused();
117
+
118
+ togglerLink(given.node.element).trigger("click");
119
+ await screen.findByText("loaded-on-demand");
120
+
121
+ expect(given.node.element).toBeSelected();
122
+ expect(given.node.element).toBeFocused();
123
+ });
124
+ });
125
+
126
+ context("when the node is not selected", () => {
127
+ it("doesn't select the node", async () => {
128
+ expect(given.node.element).not.toBeSelected();
129
+
130
+ togglerLink(given.node.element).trigger("click");
131
+ await screen.findByText("loaded-on-demand");
132
+
133
+ expect(given.node.element).not.toBeSelected();
134
+ });
135
+ });
136
+
137
+ context("when the node is selected and doesn't have the focus", () => {
138
+ beforeEach(() => {
139
+ given.$tree.tree("selectNode", given.node);
140
+ (document.activeElement as HTMLElement).blur();
141
+ });
142
+
143
+ it("keeps the node selected and not focused", async () => {
144
+ expect(given.node.element).toBeSelected();
145
+ expect(given.node.element).not.toBeFocused();
146
+
147
+ togglerLink(given.node.element).trigger("click");
148
+ await screen.findByText("loaded-on-demand");
149
+
150
+ expect(given.node.element).toBeSelected();
151
+ expect(given.node.element).not.toBeFocused();
152
+ });
153
+ });
109
154
  });
110
155
 
111
156
  context("with autoOpen is true", () => {