@treelocator/runtime 0.5.2 → 0.6.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 (163) hide show
  1. package/.eslintignore +1 -0
  2. package/dist/_generated_styles.d.ts +1 -1
  3. package/dist/_generated_styles.js +20 -0
  4. package/dist/_generated_tree_icon.d.ts +1 -1
  5. package/dist/adapters/HtmlElementTreeNode.d.ts +2 -2
  6. package/dist/adapters/HtmlElementTreeNode.js +4 -6
  7. package/dist/adapters/createTreeNode.js +17 -44
  8. package/dist/adapters/detectFramework.d.ts +8 -0
  9. package/dist/adapters/detectFramework.js +25 -0
  10. package/dist/adapters/detectFramework.test.d.ts +1 -0
  11. package/dist/adapters/detectFramework.test.js +60 -0
  12. package/dist/adapters/jsx/jsxAdapter.js +54 -89
  13. package/dist/adapters/jsx/jsxAdapter.test.d.ts +1 -0
  14. package/dist/adapters/jsx/jsxAdapter.test.js +273 -0
  15. package/dist/adapters/nextjs/parseNextjsDataAttributes.js +1 -1
  16. package/dist/adapters/nextjs/parseNextjsDataAttributes.test.d.ts +1 -0
  17. package/dist/adapters/nextjs/parseNextjsDataAttributes.test.js +158 -0
  18. package/dist/adapters/react/findFiberByHtmlElement.d.ts +1 -1
  19. package/dist/adapters/react/findFiberByHtmlElement.js +1 -1
  20. package/dist/adapters/react/getAllParentsElementsAndRootComponent.js +4 -0
  21. package/dist/adapters/resolveAdapter.d.ts +1 -1
  22. package/dist/adapters/resolveAdapter.js +4 -8
  23. package/dist/adapters/svelte/svelteAdapter.test.d.ts +1 -0
  24. package/dist/adapters/svelte/svelteAdapter.test.js +280 -0
  25. package/dist/adapters/vue/vueAdapter.test.d.ts +1 -0
  26. package/dist/adapters/vue/vueAdapter.test.js +222 -0
  27. package/dist/browserApi.d.ts +148 -0
  28. package/dist/browserApi.js +146 -5
  29. package/dist/browserApi.test.d.ts +1 -0
  30. package/dist/browserApi.test.js +287 -0
  31. package/dist/components/RecordingPillButton.d.ts +11 -0
  32. package/dist/components/RecordingPillButton.js +202 -0
  33. package/dist/components/RecordingResults.d.ts +2 -0
  34. package/dist/components/RecordingResults.js +213 -78
  35. package/dist/components/Runtime.js +161 -554
  36. package/dist/components/SettingsPanel.d.ts +5 -0
  37. package/dist/components/SettingsPanel.js +312 -0
  38. package/dist/consoleCapture.d.ts +9 -0
  39. package/dist/consoleCapture.js +95 -0
  40. package/dist/functions/cssRuleInspector.d.ts +83 -0
  41. package/dist/functions/cssRuleInspector.js +608 -0
  42. package/dist/functions/cssRuleInspector.test.d.ts +1 -0
  43. package/dist/functions/cssRuleInspector.test.js +439 -0
  44. package/dist/functions/deduplicateLabels.test.d.ts +1 -0
  45. package/dist/functions/deduplicateLabels.test.js +178 -0
  46. package/dist/functions/enrichAncestrySourceMaps.js +0 -1
  47. package/dist/functions/extractComputedStyles.d.ts +51 -0
  48. package/dist/functions/extractComputedStyles.js +447 -0
  49. package/dist/functions/extractComputedStyles.test.d.ts +1 -0
  50. package/dist/functions/extractComputedStyles.test.js +549 -0
  51. package/dist/functions/formatAncestryChain.d.ts +8 -0
  52. package/dist/functions/formatAncestryChain.js +21 -1
  53. package/dist/functions/formatAncestryChain.test.js +18 -0
  54. package/dist/functions/getUsableName.test.d.ts +1 -0
  55. package/dist/functions/getUsableName.test.js +219 -0
  56. package/dist/functions/isCombinationModifiersPressed.test.d.ts +1 -0
  57. package/dist/functions/isCombinationModifiersPressed.test.js +192 -0
  58. package/dist/functions/mergeRects.test.js +210 -1
  59. package/dist/functions/namedSnapshots.d.ts +52 -0
  60. package/dist/functions/namedSnapshots.js +161 -0
  61. package/dist/functions/namedSnapshots.test.d.ts +1 -0
  62. package/dist/functions/namedSnapshots.test.js +85 -0
  63. package/dist/functions/normalizeFilePath.test.d.ts +1 -0
  64. package/dist/functions/normalizeFilePath.test.js +66 -0
  65. package/dist/functions/parseDataId.test.d.ts +1 -0
  66. package/dist/functions/parseDataId.test.js +101 -0
  67. package/dist/hooks/getStorage.d.ts +3 -0
  68. package/dist/hooks/getStorage.js +17 -0
  69. package/dist/hooks/useEventListeners.d.ts +15 -0
  70. package/dist/hooks/useEventListeners.js +56 -0
  71. package/dist/hooks/useLocatorStorage.d.ts +18 -0
  72. package/dist/hooks/useLocatorStorage.js +41 -0
  73. package/dist/hooks/useLocatorStorage.test.d.ts +1 -0
  74. package/dist/hooks/useLocatorStorage.test.js +124 -0
  75. package/dist/hooks/useRecordingState.d.ts +43 -0
  76. package/dist/hooks/useRecordingState.js +387 -0
  77. package/dist/hooks/useSettings.d.ts +13 -0
  78. package/dist/hooks/useSettings.js +66 -0
  79. package/dist/index.d.ts +5 -2
  80. package/dist/index.js +4 -2
  81. package/dist/initRuntime.d.ts +3 -1
  82. package/dist/initRuntime.js +4 -1
  83. package/dist/mcpBridge.d.ts +61 -0
  84. package/dist/mcpBridge.js +534 -0
  85. package/dist/mcpBridge.test.d.ts +1 -0
  86. package/dist/mcpBridge.test.js +248 -0
  87. package/dist/output.css +20 -0
  88. package/dist/visualDiff/diff.d.ts +9 -0
  89. package/dist/visualDiff/diff.js +209 -0
  90. package/dist/visualDiff/diff.test.d.ts +1 -0
  91. package/dist/visualDiff/diff.test.js +253 -0
  92. package/dist/visualDiff/settle.d.ts +3 -0
  93. package/dist/visualDiff/settle.js +50 -0
  94. package/dist/visualDiff/settle.test.d.ts +1 -0
  95. package/dist/visualDiff/settle.test.js +65 -0
  96. package/dist/visualDiff/snapshot.d.ts +4 -0
  97. package/dist/visualDiff/snapshot.js +84 -0
  98. package/dist/visualDiff/snapshot.test.d.ts +1 -0
  99. package/dist/visualDiff/snapshot.test.js +245 -0
  100. package/dist/visualDiff/types.d.ts +37 -0
  101. package/dist/visualDiff/types.js +1 -0
  102. package/package.json +2 -2
  103. package/scripts/wrapCSS.js +1 -1
  104. package/scripts/wrapImage.js +1 -1
  105. package/src/_generated_styles.ts +21 -1
  106. package/src/_generated_tree_icon.ts +1 -1
  107. package/src/adapters/HtmlElementTreeNode.ts +10 -7
  108. package/src/adapters/createTreeNode.ts +12 -51
  109. package/src/adapters/detectFramework.test.ts +73 -0
  110. package/src/adapters/detectFramework.ts +28 -0
  111. package/src/adapters/jsx/jsxAdapter.test.ts +240 -0
  112. package/src/adapters/jsx/jsxAdapter.ts +53 -106
  113. package/src/adapters/nextjs/parseNextjsDataAttributes.test.ts +212 -0
  114. package/src/adapters/nextjs/parseNextjsDataAttributes.ts +1 -1
  115. package/src/adapters/react/findDebugSource.ts +5 -6
  116. package/src/adapters/react/findFiberByHtmlElement.ts +3 -3
  117. package/src/adapters/react/getAllParentsElementsAndRootComponent.ts +3 -0
  118. package/src/adapters/react/reactAdapter.ts +1 -2
  119. package/src/adapters/resolveAdapter.ts +4 -14
  120. package/src/adapters/svelte/svelteAdapter.test.ts +334 -0
  121. package/src/adapters/vue/vueAdapter.test.ts +259 -0
  122. package/src/browserApi.test.ts +329 -0
  123. package/src/browserApi.ts +351 -4
  124. package/src/components/RecordingPillButton.tsx +301 -0
  125. package/src/components/RecordingResults.tsx +114 -13
  126. package/src/components/Runtime.tsx +176 -621
  127. package/src/components/SettingsPanel.tsx +339 -0
  128. package/src/consoleCapture.ts +113 -0
  129. package/src/functions/cssRuleInspector.test.ts +517 -0
  130. package/src/functions/cssRuleInspector.ts +708 -0
  131. package/src/functions/deduplicateLabels.test.ts +115 -0
  132. package/src/functions/enrichAncestrySourceMaps.ts +6 -3
  133. package/src/functions/extractComputedStyles.test.ts +681 -0
  134. package/src/functions/extractComputedStyles.ts +768 -0
  135. package/src/functions/formatAncestryChain.test.ts +23 -1
  136. package/src/functions/formatAncestryChain.ts +22 -1
  137. package/src/functions/getUsableName.test.ts +242 -0
  138. package/src/functions/isCombinationModifiersPressed.test.ts +156 -0
  139. package/src/functions/mergeRects.test.ts +111 -1
  140. package/src/functions/namedSnapshots.test.ts +106 -0
  141. package/src/functions/namedSnapshots.ts +232 -0
  142. package/src/functions/normalizeFilePath.test.ts +80 -0
  143. package/src/functions/parseDataId.test.ts +125 -0
  144. package/src/hooks/getStorage.ts +26 -0
  145. package/src/hooks/useEventListeners.ts +97 -0
  146. package/src/hooks/useLocatorStorage.test.ts +127 -0
  147. package/src/hooks/useLocatorStorage.ts +60 -0
  148. package/src/hooks/useRecordingState.ts +516 -0
  149. package/src/hooks/useSettings.ts +83 -0
  150. package/src/index.ts +10 -5
  151. package/src/initRuntime.ts +5 -0
  152. package/src/mcpBridge.test.ts +260 -0
  153. package/src/mcpBridge.ts +677 -0
  154. package/src/visualDiff/diff.test.ts +167 -0
  155. package/src/visualDiff/diff.ts +242 -0
  156. package/src/visualDiff/settle.test.ts +77 -0
  157. package/src/visualDiff/settle.ts +62 -0
  158. package/src/visualDiff/snapshot.test.ts +200 -0
  159. package/src/visualDiff/snapshot.ts +119 -0
  160. package/src/visualDiff/types.ts +40 -0
  161. package/tsconfig.json +3 -1
  162. package/vitest.config.ts +18 -0
  163. package/jest.config.ts +0 -195
@@ -2,7 +2,7 @@ import reactAdapter from "./react/reactAdapter";
2
2
  import jsxAdapter from "./jsx/jsxAdapter";
3
3
  import svelteAdapter from "./svelte/svelteAdapter";
4
4
  import vueAdapter from "./vue/vueAdapter";
5
- import { detectJSX, detectReact, detectSvelte, detectVue } from "@locator/shared";
5
+ import { detectFramework } from "./detectFramework";
6
6
  const adapterMap = {
7
7
  react: reactAdapter,
8
8
  svelte: svelteAdapter,
@@ -13,16 +13,12 @@ const adapterMap = {
13
13
  /**
14
14
  * Resolve the framework adapter to use.
15
15
  * If an explicit adapterId is given, return that adapter.
16
- * Otherwise, auto-detect the framework in priority order.
16
+ * Otherwise, auto-detect the framework.
17
17
  */
18
18
  export function resolveAdapter(adapterId) {
19
19
  if (adapterId) {
20
20
  return adapterMap[adapterId] ?? null;
21
21
  }
22
- if (detectSvelte()) return svelteAdapter;
23
- if (detectVue()) return vueAdapter;
24
- if (detectReact()) return reactAdapter;
25
- // Must be last: global data leaks from the LocatorJS extension (SolidJS + JSX plugin in dev)
26
- if (detectJSX()) return jsxAdapter;
27
- return null;
22
+ const framework = detectFramework();
23
+ return framework ? adapterMap[framework] ?? null : null;
28
24
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,280 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { getElementInfo, SvelteTreeNodeElement } from "./svelteAdapter";
3
+ function createMockSvelteElement(metadata) {
4
+ const el = document.createElement("div");
5
+ el.getBoundingClientRect = () => ({
6
+ x: 0,
7
+ y: 0,
8
+ width: 100,
9
+ height: 50,
10
+ top: 0,
11
+ left: 0,
12
+ right: 100,
13
+ bottom: 50,
14
+ toJSON: () => ({})
15
+ });
16
+ if (metadata) {
17
+ el.__svelte_meta = {
18
+ loc: metadata
19
+ };
20
+ }
21
+ return el;
22
+ }
23
+ describe("Svelte Adapter - getElementInfo", () => {
24
+ test("returns null when element has no __svelte_meta", () => {
25
+ const el = createMockSvelteElement(null);
26
+ expect(getElementInfo(el)).toBeNull();
27
+ });
28
+ test("returns FullElementInfo with correct filePath from __svelte_meta.loc.file", () => {
29
+ const el = createMockSvelteElement({
30
+ char: 0,
31
+ column: 5,
32
+ file: "src/App.svelte",
33
+ line: 10
34
+ });
35
+ const info = getElementInfo(el);
36
+ expect(info).not.toBeNull();
37
+ expect(info.thisElement.link.filePath).toBe("src/App.svelte");
38
+ });
39
+ test("returns FullElementInfo with line number incremented by 1 from __svelte_meta.loc.line", () => {
40
+ const el = createMockSvelteElement({
41
+ char: 0,
42
+ column: 5,
43
+ file: "src/App.svelte",
44
+ line: 10
45
+ });
46
+ const info = getElementInfo(el);
47
+ expect(info).not.toBeNull();
48
+ expect(info.thisElement.link.line).toBe(11); // 10 + 1
49
+ });
50
+ test("returns FullElementInfo with column number incremented by 1 from __svelte_meta.loc.column", () => {
51
+ const el = createMockSvelteElement({
52
+ char: 0,
53
+ column: 5,
54
+ file: "src/App.svelte",
55
+ line: 10
56
+ });
57
+ const info = getElementInfo(el);
58
+ expect(info).not.toBeNull();
59
+ expect(info.thisElement.link.column).toBe(6); // 5 + 1
60
+ });
61
+ test("returns FullElementInfo with empty componentsLabels array", () => {
62
+ const el = createMockSvelteElement({
63
+ char: 0,
64
+ column: 0,
65
+ file: "src/Button.svelte",
66
+ line: 5
67
+ });
68
+ const info = getElementInfo(el);
69
+ expect(info).not.toBeNull();
70
+ expect(info.componentsLabels).toEqual([]);
71
+ });
72
+ test("returns FullElementInfo with htmlElement pointing to the correct element", () => {
73
+ const el = createMockSvelteElement({
74
+ char: 0,
75
+ column: 0,
76
+ file: "src/Card.svelte",
77
+ line: 15
78
+ });
79
+ const info = getElementInfo(el);
80
+ expect(info).not.toBeNull();
81
+ expect(info.htmlElement).toBe(el);
82
+ });
83
+ test("returns FullElementInfo with element label as lowercase nodeName", () => {
84
+ const el = createMockSvelteElement({
85
+ char: 0,
86
+ column: 0,
87
+ file: "src/Button.svelte",
88
+ line: 5
89
+ });
90
+ const info = getElementInfo(el);
91
+ expect(info).not.toBeNull();
92
+ expect(info.thisElement.label).toBe("div"); // createElement creates a div
93
+ });
94
+ test("returns FullElementInfo with parentElements as empty array", () => {
95
+ const el = createMockSvelteElement({
96
+ char: 0,
97
+ column: 0,
98
+ file: "src/Layout.svelte",
99
+ line: 20
100
+ });
101
+ const info = getElementInfo(el);
102
+ expect(info).not.toBeNull();
103
+ expect(info.parentElements).toEqual([]);
104
+ });
105
+ test("handles different line and column values correctly", () => {
106
+ const el1 = createMockSvelteElement({
107
+ char: 0,
108
+ column: 0,
109
+ file: "src/File1.svelte",
110
+ line: 0
111
+ });
112
+ const el2 = createMockSvelteElement({
113
+ char: 100,
114
+ column: 20,
115
+ file: "src/File2.svelte",
116
+ line: 99
117
+ });
118
+ const info1 = getElementInfo(el1);
119
+ const info2 = getElementInfo(el2);
120
+ expect(info1.thisElement.link.line).toBe(1); // 0 + 1
121
+ expect(info1.thisElement.link.column).toBe(1); // 0 + 1
122
+ expect(info2.thisElement.link.line).toBe(100); // 99 + 1
123
+ expect(info2.thisElement.link.column).toBe(21); // 20 + 1
124
+ });
125
+ test("returns FullElementInfo with projectPath as empty string", () => {
126
+ const el = createMockSvelteElement({
127
+ char: 0,
128
+ column: 0,
129
+ file: "src/App.svelte",
130
+ line: 10
131
+ });
132
+ const info = getElementInfo(el);
133
+ expect(info).not.toBeNull();
134
+ expect(info.thisElement.link.projectPath).toBe("");
135
+ });
136
+ });
137
+ describe("SvelteTreeNodeElement.getSource", () => {
138
+ test("returns null when element has no __svelte_meta", () => {
139
+ const el = createMockSvelteElement(null);
140
+ const node = new SvelteTreeNodeElement(el);
141
+ expect(node.getSource()).toBeNull();
142
+ });
143
+ test("returns Source with correct fileName from __svelte_meta.loc.file", () => {
144
+ const el = createMockSvelteElement({
145
+ char: 0,
146
+ column: 5,
147
+ file: "src/App.svelte",
148
+ line: 10
149
+ });
150
+ const node = new SvelteTreeNodeElement(el);
151
+ const source = node.getSource();
152
+ expect(source).not.toBeNull();
153
+ expect(source.fileName).toBe("src/App.svelte");
154
+ });
155
+ test("returns Source with lineNumber incremented by 1 from __svelte_meta.loc.line", () => {
156
+ const el = createMockSvelteElement({
157
+ char: 0,
158
+ column: 5,
159
+ file: "src/App.svelte",
160
+ line: 10
161
+ });
162
+ const node = new SvelteTreeNodeElement(el);
163
+ const source = node.getSource();
164
+ expect(source).not.toBeNull();
165
+ expect(source.lineNumber).toBe(11); // 10 + 1
166
+ });
167
+ test("returns Source with columnNumber incremented by 1 from __svelte_meta.loc.column", () => {
168
+ const el = createMockSvelteElement({
169
+ char: 0,
170
+ column: 5,
171
+ file: "src/App.svelte",
172
+ line: 10
173
+ });
174
+ const node = new SvelteTreeNodeElement(el);
175
+ const source = node.getSource();
176
+ expect(source).not.toBeNull();
177
+ expect(source.columnNumber).toBe(6); // 5 + 1
178
+ });
179
+ test("returns Source from different Svelte components", () => {
180
+ const el1 = createMockSvelteElement({
181
+ char: 0,
182
+ column: 0,
183
+ file: "src/Header.svelte",
184
+ line: 5
185
+ });
186
+ const el2 = createMockSvelteElement({
187
+ char: 0,
188
+ column: 0,
189
+ file: "src/Footer.svelte",
190
+ line: 20
191
+ });
192
+ const node1 = new SvelteTreeNodeElement(el1);
193
+ const node2 = new SvelteTreeNodeElement(el2);
194
+ const source1 = node1.getSource();
195
+ const source2 = node2.getSource();
196
+ expect(source1.fileName).toBe("src/Header.svelte");
197
+ expect(source2.fileName).toBe("src/Footer.svelte");
198
+ expect(source1.lineNumber).toBe(6); // 5 + 1
199
+ expect(source2.lineNumber).toBe(21); // 20 + 1
200
+ });
201
+ test("handles line and column value of 0 correctly (increments to 1)", () => {
202
+ const el = createMockSvelteElement({
203
+ char: 0,
204
+ column: 0,
205
+ file: "src/App.svelte",
206
+ line: 0
207
+ });
208
+ const node = new SvelteTreeNodeElement(el);
209
+ const source = node.getSource();
210
+ expect(source).not.toBeNull();
211
+ expect(source.lineNumber).toBe(1);
212
+ expect(source.columnNumber).toBe(1);
213
+ });
214
+ });
215
+ describe("SvelteTreeNodeElement.getComponent", () => {
216
+ test("always returns null", () => {
217
+ const el = createMockSvelteElement({
218
+ char: 0,
219
+ column: 5,
220
+ file: "src/App.svelte",
221
+ line: 10
222
+ });
223
+ const node = new SvelteTreeNodeElement(el);
224
+ expect(node.getComponent()).toBeNull();
225
+ });
226
+ test("returns null even when __svelte_meta is present", () => {
227
+ const el = createMockSvelteElement({
228
+ char: 0,
229
+ column: 0,
230
+ file: "src/Button.svelte",
231
+ line: 5
232
+ });
233
+ const node = new SvelteTreeNodeElement(el);
234
+ expect(node.getComponent()).toBeNull();
235
+ });
236
+ test("returns null when element has no __svelte_meta", () => {
237
+ const el = createMockSvelteElement(null);
238
+ const node = new SvelteTreeNodeElement(el);
239
+ expect(node.getComponent()).toBeNull();
240
+ });
241
+ });
242
+ describe("SvelteTreeNodeElement inheritance and structure", () => {
243
+ test("extends HtmlElementTreeNode with custom getSource and getComponent", () => {
244
+ const el = createMockSvelteElement({
245
+ char: 0,
246
+ column: 0,
247
+ file: "src/App.svelte",
248
+ line: 10
249
+ });
250
+ const node = new SvelteTreeNodeElement(el);
251
+
252
+ // Should inherit from HtmlElementTreeNode
253
+ expect(node.type).toBe("element");
254
+ expect(node.name).toBe("div");
255
+ expect(node.element).toBe(el);
256
+ });
257
+ test("getBox returns element's bounding client rect", () => {
258
+ const el = createMockSvelteElement({
259
+ char: 0,
260
+ column: 0,
261
+ file: "src/App.svelte",
262
+ line: 10
263
+ });
264
+ const node = new SvelteTreeNodeElement(el);
265
+ const box = node.getBox();
266
+ expect(box).not.toBeNull();
267
+ expect(box.width).toBe(100);
268
+ expect(box.height).toBe(50);
269
+ });
270
+ test("getElement returns the underlying HTMLElement", () => {
271
+ const el = createMockSvelteElement({
272
+ char: 0,
273
+ column: 0,
274
+ file: "src/App.svelte",
275
+ line: 10
276
+ });
277
+ const node = new SvelteTreeNodeElement(el);
278
+ expect(node.getElement()).toBe(el);
279
+ });
280
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,222 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { getElementInfo, VueTreeNodeElement } from "./vueAdapter";
3
+ function createMockVueElement(props) {
4
+ const el = document.createElement("div");
5
+ el.getBoundingClientRect = () => ({
6
+ x: 0,
7
+ y: 0,
8
+ width: 100,
9
+ height: 50,
10
+ top: 0,
11
+ left: 0,
12
+ right: 100,
13
+ bottom: 50,
14
+ toJSON: () => ({})
15
+ });
16
+ if (props) {
17
+ el.__vueParentComponent = {
18
+ type: {
19
+ __file: props.file,
20
+ __name: props.name
21
+ }
22
+ };
23
+ }
24
+ return el;
25
+ }
26
+ describe("Vue Adapter - getElementInfo", () => {
27
+ test("returns null when element has no __vueParentComponent", () => {
28
+ const el = createMockVueElement(null);
29
+ expect(getElementInfo(el)).toBeNull();
30
+ });
31
+ test("returns FullElementInfo with correct filePath when __vueParentComponent has type.__file", () => {
32
+ const el = createMockVueElement({
33
+ file: "/src/MyComponent.vue",
34
+ name: "MyComponent"
35
+ });
36
+ const info = getElementInfo(el);
37
+ expect(info).not.toBeNull();
38
+ expect(info.thisElement.link.filePath).toBe("/src/MyComponent.vue");
39
+ });
40
+ test("returns FullElementInfo with correct component name when __vueParentComponent has type.__name", () => {
41
+ const el = createMockVueElement({
42
+ file: "/src/Button.vue",
43
+ name: "Button"
44
+ });
45
+ const info = getElementInfo(el);
46
+ expect(info).not.toBeNull();
47
+ expect(info.componentsLabels[0].label).toBe("Button");
48
+ });
49
+ test("returns null when __vueParentComponent.type is missing", () => {
50
+ const el = createMockVueElement(null);
51
+ el.__vueParentComponent = {};
52
+ expect(getElementInfo(el)).toBeNull();
53
+ });
54
+ test("returns null when __vueParentComponent.type.__file is missing", () => {
55
+ const el = createMockVueElement(null);
56
+ el.__vueParentComponent = {
57
+ type: {
58
+ __name: "MyComponent"
59
+ }
60
+ };
61
+ expect(getElementInfo(el)).toBeNull();
62
+ });
63
+ test("returns null when __vueParentComponent.type.__name is missing", () => {
64
+ const el = createMockVueElement(null);
65
+ el.__vueParentComponent = {
66
+ type: {
67
+ __file: "/src/MyComponent.vue"
68
+ }
69
+ };
70
+ expect(getElementInfo(el)).toBeNull();
71
+ });
72
+ test("returns FullElementInfo with htmlElement pointing to the correct element", () => {
73
+ const el = createMockVueElement({
74
+ file: "/src/Card.vue",
75
+ name: "Card"
76
+ });
77
+ const info = getElementInfo(el);
78
+ expect(info).not.toBeNull();
79
+ expect(info.htmlElement).toBe(el);
80
+ });
81
+ test("returns FullElementInfo with element label as lowercase nodeName", () => {
82
+ const el = createMockVueElement({
83
+ file: "/src/Button.vue",
84
+ name: "Button"
85
+ });
86
+ const info = getElementInfo(el);
87
+ expect(info).not.toBeNull();
88
+ expect(info.thisElement.label).toBe("div"); // createElement creates a div
89
+ });
90
+ test("returns FullElementInfo with default line and column of 1", () => {
91
+ const el = createMockVueElement({
92
+ file: "/src/App.vue",
93
+ name: "App"
94
+ });
95
+ const info = getElementInfo(el);
96
+ expect(info).not.toBeNull();
97
+ expect(info.thisElement.link.line).toBe(1);
98
+ expect(info.thisElement.link.column).toBe(1);
99
+ });
100
+ test("handles nested Vue component structure with subTree", () => {
101
+ const el = createMockVueElement({
102
+ file: "/src/Layout.vue",
103
+ name: "Layout"
104
+ });
105
+
106
+ // Add a mock subTree (though getElementInfo doesn't use it in the basic case)
107
+ el.__vueParentComponent.subTree = {
108
+ el
109
+ };
110
+ const info = getElementInfo(el);
111
+ expect(info).not.toBeNull();
112
+ expect(info.componentsLabels[0].label).toBe("Layout");
113
+ });
114
+ });
115
+ describe("VueTreeNodeElement.getSource", () => {
116
+ test("returns null when element has no __vueParentComponent", () => {
117
+ const el = createMockVueElement(null);
118
+ const node = new VueTreeNodeElement(el);
119
+ expect(node.getSource()).toBeNull();
120
+ });
121
+ test("returns Source with correct fileName from __vueParentComponent.type.__file", () => {
122
+ const el = createMockVueElement({
123
+ file: "/src/MyComponent.vue",
124
+ name: "MyComponent"
125
+ });
126
+ const node = new VueTreeNodeElement(el);
127
+ const source = node.getSource();
128
+ expect(source).not.toBeNull();
129
+ expect(source.fileName).toBe("/src/MyComponent.vue");
130
+ });
131
+ test("returns Source with lineNumber and columnNumber of 1", () => {
132
+ const el = createMockVueElement({
133
+ file: "/src/Button.vue",
134
+ name: "Button"
135
+ });
136
+ const node = new VueTreeNodeElement(el);
137
+ const source = node.getSource();
138
+ expect(source).not.toBeNull();
139
+ expect(source.lineNumber).toBe(1);
140
+ expect(source.columnNumber).toBe(1);
141
+ });
142
+ test("returns null when __vueParentComponent.type is missing", () => {
143
+ const el = createMockVueElement(null);
144
+ el.__vueParentComponent = {};
145
+ const node = new VueTreeNodeElement(el);
146
+ expect(node.getSource()).toBeNull();
147
+ });
148
+ test("returns null when __vueParentComponent.type.__file is missing", () => {
149
+ const el = createMockVueElement(null);
150
+ el.__vueParentComponent = {
151
+ type: {
152
+ __name: "MyComponent"
153
+ }
154
+ };
155
+ const node = new VueTreeNodeElement(el);
156
+ expect(node.getSource()).toBeNull();
157
+ });
158
+ test("returns Source from different Vue components", () => {
159
+ const el1 = createMockVueElement({
160
+ file: "/src/Header.vue",
161
+ name: "Header"
162
+ });
163
+ const el2 = createMockVueElement({
164
+ file: "/src/Footer.vue",
165
+ name: "Footer"
166
+ });
167
+ const node1 = new VueTreeNodeElement(el1);
168
+ const node2 = new VueTreeNodeElement(el2);
169
+ const source1 = node1.getSource();
170
+ const source2 = node2.getSource();
171
+ expect(source1.fileName).toBe("/src/Header.vue");
172
+ expect(source2.fileName).toBe("/src/Footer.vue");
173
+ });
174
+ });
175
+ describe("VueTreeNodeElement.getComponent", () => {
176
+ test("always returns null", () => {
177
+ const el = createMockVueElement({
178
+ file: "/src/MyComponent.vue",
179
+ name: "MyComponent"
180
+ });
181
+ const node = new VueTreeNodeElement(el);
182
+ expect(node.getComponent()).toBeNull();
183
+ });
184
+ test("returns null even when __vueParentComponent is present", () => {
185
+ const el = createMockVueElement({
186
+ file: "/src/Button.vue",
187
+ name: "Button"
188
+ });
189
+ const node = new VueTreeNodeElement(el);
190
+ expect(node.getComponent()).toBeNull();
191
+ });
192
+ test("returns null when element has no __vueParentComponent", () => {
193
+ const el = createMockVueElement(null);
194
+ const node = new VueTreeNodeElement(el);
195
+ expect(node.getComponent()).toBeNull();
196
+ });
197
+ });
198
+ describe("VueTreeNodeElement inheritance and structure", () => {
199
+ test("extends HtmlElementTreeNode with custom getSource and getComponent", () => {
200
+ const el = createMockVueElement({
201
+ file: "/src/App.vue",
202
+ name: "App"
203
+ });
204
+ const node = new VueTreeNodeElement(el);
205
+
206
+ // Should inherit from HtmlElementTreeNode
207
+ expect(node.type).toBe("element");
208
+ expect(node.name).toBe("div");
209
+ expect(node.element).toBe(el);
210
+ });
211
+ test("getBox returns element's bounding client rect", () => {
212
+ const el = createMockVueElement({
213
+ file: "/src/App.vue",
214
+ name: "App"
215
+ });
216
+ const node = new VueTreeNodeElement(el);
217
+ const box = node.getBox();
218
+ expect(box).not.toBeNull();
219
+ expect(box.width).toBe(100);
220
+ expect(box.height).toBe(50);
221
+ });
222
+ });