@syntrologie/adapt-nav 2.5.1 → 2.6.0-canary.2

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 (76) hide show
  1. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +1 -1
  2. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.js +22 -153
  3. package/node_modules/@syntrologie/shared-editor-ui/package.json +2 -2
  4. package/package.json +1 -1
  5. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts +0 -2
  6. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts.map +0 -1
  7. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.js +0 -224
  8. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/BeforeAfterToggle.test.d.ts +0 -2
  9. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/BeforeAfterToggle.test.d.ts.map +0 -1
  10. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/BeforeAfterToggle.test.js +0 -29
  11. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.d.ts +0 -2
  12. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.d.ts.map +0 -1
  13. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.js +0 -260
  14. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DetectionBadge.test.d.ts +0 -2
  15. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DetectionBadge.test.d.ts.map +0 -1
  16. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DetectionBadge.test.js +0 -70
  17. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.d.ts +0 -2
  18. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.d.ts.map +0 -1
  19. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.js +0 -46
  20. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditBackButton.test.d.ts +0 -2
  21. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditBackButton.test.d.ts.map +0 -1
  22. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditBackButton.test.js +0 -20
  23. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorBody.test.d.ts +0 -2
  24. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorBody.test.d.ts.map +0 -1
  25. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorBody.test.js +0 -12
  26. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.d.ts +0 -2
  27. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.d.ts.map +0 -1
  28. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.js +0 -84
  29. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorFooter.test.d.ts +0 -2
  30. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorFooter.test.d.ts.map +0 -1
  31. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorFooter.test.js +0 -23
  32. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorHeader.test.d.ts +0 -2
  33. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorHeader.test.d.ts.map +0 -1
  34. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorHeader.test.js +0 -23
  35. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorInput.test.d.ts +0 -2
  36. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorInput.test.d.ts.map +0 -1
  37. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorInput.test.js +0 -26
  38. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorLayout.test.d.ts +0 -2
  39. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorLayout.test.d.ts.map +0 -1
  40. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorLayout.test.js +0 -13
  41. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.d.ts +0 -2
  42. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.d.ts.map +0 -1
  43. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.js +0 -496
  44. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorSelect.test.d.ts +0 -2
  45. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorSelect.test.d.ts.map +0 -1
  46. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorSelect.test.js +0 -22
  47. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorTextarea.test.d.ts +0 -2
  48. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorTextarea.test.d.ts.map +0 -1
  49. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorTextarea.test.js +0 -20
  50. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.d.ts +0 -2
  51. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.d.ts.map +0 -1
  52. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.js +0 -176
  53. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EmptyState.test.d.ts +0 -2
  54. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EmptyState.test.d.ts.map +0 -1
  55. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EmptyState.test.js +0 -10
  56. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/GroupHeader.test.d.ts +0 -2
  57. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/GroupHeader.test.d.ts.map +0 -1
  58. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/GroupHeader.test.js +0 -14
  59. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/TriggerJourney.test.d.ts +0 -2
  60. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/TriggerJourney.test.d.ts.map +0 -1
  61. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/TriggerJourney.test.js +0 -189
  62. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/cn.test.d.ts +0 -2
  63. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/cn.test.d.ts.map +0 -1
  64. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/cn.test.js +0 -16
  65. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/formatConditionLabel.test.d.ts +0 -2
  66. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/formatConditionLabel.test.d.ts.map +0 -1
  67. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/formatConditionLabel.test.js +0 -329
  68. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts +0 -2
  69. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts.map +0 -1
  70. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.js +0 -257
  71. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useElementRect.test.d.ts +0 -2
  72. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useElementRect.test.d.ts.map +0 -1
  73. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useElementRect.test.js +0 -112
  74. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts +0 -2
  75. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts.map +0 -1
  76. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.js +0 -1015
@@ -1,329 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { formatConditionLabel } from '../formatConditionLabel';
3
- describe('formatConditionLabel', () => {
4
- it('event_count: returns label with progress data', () => {
5
- const result = formatConditionLabel({ type: 'event_count', key: 'click', operator: 'gte', count: 3 }, 1);
6
- expect(result.label).toBe('click \u2265 3');
7
- expect(result.progress).toEqual({ current: 1, target: 3, operator: 'gte' });
8
- // New fields must be present
9
- expect(result.instruction).toBeDefined();
10
- expect(result.shortLabel).toBeDefined();
11
- });
12
- it('page_url: returns the url as label', () => {
13
- const result = formatConditionLabel({ type: 'page_url', url: '/pricing' });
14
- expect(result.label).toBe('/pricing');
15
- });
16
- it('viewport: returns width constraints as label', () => {
17
- const result = formatConditionLabel({ type: 'viewport', maxWidth: 768 });
18
- expect(result.label).toContain('w');
19
- expect(result.label).toContain('768px');
20
- });
21
- it('viewport: returns "any viewport" when no constraints', () => {
22
- const result = formatConditionLabel({ type: 'viewport' });
23
- expect(result.label).toBe('any viewport');
24
- });
25
- it('dismissed (inverted): returns "not dismissed"', () => {
26
- const result = formatConditionLabel({ type: 'dismissed', inverted: true });
27
- expect(result.label).toBe('not dismissed');
28
- });
29
- it('cooldown_active (inverted): returns "cooldown inactive"', () => {
30
- const result = formatConditionLabel({ type: 'cooldown_active', inverted: true });
31
- expect(result.label).toBe('cooldown inactive');
32
- });
33
- it('unknown type: returns type name as label', () => {
34
- const result = formatConditionLabel({ type: 'some_custom' });
35
- expect(result.label).toBe('some_custom');
36
- });
37
- });
38
- describe('instruction + shortLabel fields', () => {
39
- describe('event_count', () => {
40
- it('compound key with "page-views" returns instruction with topic and times', () => {
41
- const result = formatConditionLabel({ type: 'event_count', key: 'fine-arts-page-views', operator: 'gte', count: 2 }, 0);
42
- expect(result.instruction).toContain('fine arts');
43
- expect(result.instruction).toContain('2+ times');
44
- });
45
- it('compound key shortLabel contains "2+ times"', () => {
46
- const result = formatConditionLabel({ type: 'event_count', key: 'fine-arts-page-views', operator: 'gte', count: 2 }, 0);
47
- expect(result.shortLabel).toContain('2+ times');
48
- });
49
- it('simple "click" key returns instruction mentioning click', () => {
50
- const result = formatConditionLabel({ type: 'event_count', key: 'click', operator: 'gte', count: 3 }, 1);
51
- expect(result.instruction.toLowerCase()).toContain('click');
52
- });
53
- it('click key shortLabel contains "clicks"', () => {
54
- const result = formatConditionLabel({ type: 'event_count', key: 'click', operator: 'gte', count: 3 }, 1);
55
- expect(result.shortLabel).toContain('click');
56
- });
57
- it('generic key returns instruction with "events"', () => {
58
- const result = formatConditionLabel({ type: 'event_count', key: 'scroll-depth', operator: 'gte', count: 5 }, 0);
59
- expect(result.instruction.toLowerCase()).toContain('event');
60
- });
61
- });
62
- describe('page_url', () => {
63
- it('url with wildcards returns instruction starting with "Visit"', () => {
64
- const result = formatConditionLabel({
65
- type: 'page_url',
66
- url: '/fine-arts/**',
67
- });
68
- expect(result.instruction).toMatch(/^Visit/);
69
- });
70
- it('url with wildcards cleans up trailing wildcards', () => {
71
- const result = formatConditionLabel({
72
- type: 'page_url',
73
- url: '/fine-arts/**',
74
- });
75
- expect(result.instruction).not.toContain('**');
76
- });
77
- it('simple url returns instruction starting with "Visit"', () => {
78
- const result = formatConditionLabel({
79
- type: 'page_url',
80
- url: '/pricing',
81
- });
82
- expect(result.instruction).toMatch(/^Visit/);
83
- });
84
- it('shortLabel contains cleaned url', () => {
85
- const result = formatConditionLabel({
86
- type: 'page_url',
87
- url: '/fine-arts/**',
88
- });
89
- expect(result.shortLabel).toContain('/fine-arts/');
90
- expect(result.shortLabel).not.toContain('**');
91
- });
92
- });
93
- describe('anchor_visible', () => {
94
- it('instruction starts with "Scroll"', () => {
95
- const result = formatConditionLabel({
96
- type: 'anchor_visible',
97
- anchorId: 'hero-section',
98
- state: 'visible',
99
- });
100
- expect(result.instruction).toMatch(/^Scroll/);
101
- });
102
- it('instruction contains anchorId and state', () => {
103
- const result = formatConditionLabel({
104
- type: 'anchor_visible',
105
- anchorId: 'hero-section',
106
- state: 'visible',
107
- });
108
- expect(result.instruction).toContain('hero-section');
109
- expect(result.instruction).toContain('visible');
110
- });
111
- it('shortLabel contains anchorId', () => {
112
- const result = formatConditionLabel({
113
- type: 'anchor_visible',
114
- anchorId: 'hero-section',
115
- state: 'visible',
116
- });
117
- expect(result.shortLabel).toContain('hero-section');
118
- });
119
- });
120
- describe('event_occurred', () => {
121
- it('instruction contains "event"', () => {
122
- const result = formatConditionLabel({
123
- type: 'event_occurred',
124
- eventName: 'signup_complete',
125
- });
126
- expect(result.instruction.toLowerCase()).toContain('event');
127
- });
128
- it('instruction contains event name', () => {
129
- const result = formatConditionLabel({
130
- type: 'event_occurred',
131
- eventName: 'signup_complete',
132
- });
133
- expect(result.instruction).toContain('signup_complete');
134
- });
135
- it('shortLabel contains event name', () => {
136
- const result = formatConditionLabel({
137
- type: 'event_occurred',
138
- eventName: 'signup_complete',
139
- });
140
- expect(result.shortLabel).toContain('signup_complete');
141
- });
142
- });
143
- describe('viewport', () => {
144
- it('viewport with minWidth instruction contains pixel value', () => {
145
- const result = formatConditionLabel({
146
- type: 'viewport',
147
- minWidth: 768,
148
- });
149
- expect(result.instruction).toContain('768px');
150
- });
151
- it('viewport instruction starts with "Use a viewport"', () => {
152
- const result = formatConditionLabel({
153
- type: 'viewport',
154
- minWidth: 768,
155
- });
156
- expect(result.instruction).toMatch(/^Use a viewport/);
157
- });
158
- it('viewport with no constraints shortLabel is "Any size"', () => {
159
- const result = formatConditionLabel({ type: 'viewport' });
160
- expect(result.shortLabel).toBe('Any size');
161
- });
162
- });
163
- describe('session_metric', () => {
164
- it('instruction contains key and threshold', () => {
165
- const result = formatConditionLabel({
166
- type: 'session_metric',
167
- key: 'time_on_page',
168
- operator: 'gte',
169
- threshold: 30,
170
- });
171
- expect(result.instruction).toContain('time_on_page');
172
- expect(result.instruction).toContain('30');
173
- });
174
- it('shortLabel contains key and threshold', () => {
175
- const result = formatConditionLabel({
176
- type: 'session_metric',
177
- key: 'time_on_page',
178
- operator: 'gte',
179
- threshold: 30,
180
- });
181
- expect(result.shortLabel).toContain('time_on_page');
182
- expect(result.shortLabel).toContain('30');
183
- });
184
- });
185
- describe('dismissed', () => {
186
- it('inverted instruction contains "Do not dismiss" or "not dismiss"', () => {
187
- const result = formatConditionLabel({
188
- type: 'dismissed',
189
- inverted: true,
190
- key: 'promo-banner',
191
- });
192
- expect(result.instruction.toLowerCase()).toContain('not dismiss');
193
- });
194
- it('non-inverted instruction contains "Dismiss"', () => {
195
- const result = formatConditionLabel({
196
- type: 'dismissed',
197
- inverted: false,
198
- key: 'promo-banner',
199
- });
200
- expect(result.instruction).toContain('Dismiss');
201
- });
202
- it('inverted shortLabel contains "Not dismissed"', () => {
203
- const result = formatConditionLabel({
204
- type: 'dismissed',
205
- inverted: true,
206
- key: 'promo-banner',
207
- });
208
- expect(result.shortLabel).toBe('Not dismissed');
209
- });
210
- it('non-inverted shortLabel contains "Dismissed"', () => {
211
- const result = formatConditionLabel({
212
- type: 'dismissed',
213
- inverted: false,
214
- key: 'promo-banner',
215
- });
216
- expect(result.shortLabel).toBe('Dismissed');
217
- });
218
- });
219
- describe('cooldown_active', () => {
220
- it('inverted instruction contains "Wait for cooldown"', () => {
221
- const result = formatConditionLabel({
222
- type: 'cooldown_active',
223
- inverted: true,
224
- });
225
- expect(result.instruction).toContain('Wait for cooldown');
226
- });
227
- it('non-inverted instruction contains "Cooldown is active"', () => {
228
- const result = formatConditionLabel({
229
- type: 'cooldown_active',
230
- inverted: false,
231
- });
232
- expect(result.instruction).toContain('Cooldown is active');
233
- });
234
- it('inverted shortLabel is "Cooldown done"', () => {
235
- const result = formatConditionLabel({
236
- type: 'cooldown_active',
237
- inverted: true,
238
- });
239
- expect(result.shortLabel).toBe('Cooldown done');
240
- });
241
- it('non-inverted shortLabel is "Cooldown active"', () => {
242
- const result = formatConditionLabel({
243
- type: 'cooldown_active',
244
- inverted: false,
245
- });
246
- expect(result.shortLabel).toBe('Cooldown active');
247
- });
248
- });
249
- describe('frequency_limit', () => {
250
- it('inverted instruction contains "fewer than"', () => {
251
- const result = formatConditionLabel({
252
- type: 'frequency_limit',
253
- inverted: true,
254
- limit: 5,
255
- });
256
- expect(result.instruction).toContain('fewer than');
257
- });
258
- it('non-inverted instruction contains "Shown"', () => {
259
- const result = formatConditionLabel({
260
- type: 'frequency_limit',
261
- inverted: false,
262
- limit: 5,
263
- });
264
- expect(result.instruction).toContain('Shown');
265
- });
266
- it('inverted shortLabel is "< N views"', () => {
267
- const result = formatConditionLabel({
268
- type: 'frequency_limit',
269
- inverted: true,
270
- limit: 5,
271
- });
272
- expect(result.shortLabel).toBe('< 5 views');
273
- });
274
- it('non-inverted shortLabel is "N+ views"', () => {
275
- const result = formatConditionLabel({
276
- type: 'frequency_limit',
277
- inverted: false,
278
- limit: 5,
279
- });
280
- expect(result.shortLabel).toBe('5+ views');
281
- });
282
- });
283
- describe('route', () => {
284
- it('instruction contains "Navigate to"', () => {
285
- const result = formatConditionLabel({
286
- type: 'route',
287
- routeId: 'checkout',
288
- });
289
- expect(result.instruction).toContain('Navigate to');
290
- expect(result.instruction).toContain('checkout');
291
- });
292
- it('shortLabel is routeId', () => {
293
- const result = formatConditionLabel({
294
- type: 'route',
295
- routeId: 'checkout',
296
- });
297
- expect(result.shortLabel).toBe('checkout');
298
- });
299
- });
300
- describe('state_equals', () => {
301
- it('instruction contains key and value', () => {
302
- const result = formatConditionLabel({
303
- type: 'state_equals',
304
- key: 'theme',
305
- value: 'dark',
306
- });
307
- expect(result.instruction).toContain('theme');
308
- expect(result.instruction).toContain('dark');
309
- });
310
- it('shortLabel is key', () => {
311
- const result = formatConditionLabel({
312
- type: 'state_equals',
313
- key: 'theme',
314
- value: 'dark',
315
- });
316
- expect(result.shortLabel).toBe('theme');
317
- });
318
- });
319
- describe('unknown type', () => {
320
- it('instruction equals the type string', () => {
321
- const result = formatConditionLabel({ type: 'some_custom' });
322
- expect(result.instruction).toBe('some_custom');
323
- });
324
- it('shortLabel equals the type string', () => {
325
- const result = formatConditionLabel({ type: 'some_custom' });
326
- expect(result.shortLabel).toBe('some_custom');
327
- });
328
- });
329
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=selectorGenerator.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selectorGenerator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/selectorGenerator.test.ts"],"names":[],"mappings":""}
@@ -1,257 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- // Shim CSS.escape for jsdom (not available by default)
3
- if (typeof CSS === 'undefined') {
4
- globalThis.CSS = { escape: (str) => str.replace(/([^\w-])/g, '\\$1') };
5
- }
6
- else if (!CSS.escape) {
7
- CSS.escape = (str) => str.replace(/([^\w-])/g, '\\$1');
8
- }
9
- import { generateSelector, getElementDescription, validateSelector, } from '../utils/selectorGenerator';
10
- // =============================================================================
11
- // Mock css-selector-generator
12
- // =============================================================================
13
- vi.mock('css-selector-generator', () => ({
14
- getCssSelector: vi.fn(),
15
- }));
16
- import { getCssSelector } from 'css-selector-generator';
17
- const mockGetCssSelector = vi.mocked(getCssSelector);
18
- // =============================================================================
19
- // HELPERS
20
- // =============================================================================
21
- function createElement(tag, attrs = {}) {
22
- const el = document.createElement(tag);
23
- for (const [key, value] of Object.entries(attrs)) {
24
- if (key === 'textContent') {
25
- el.textContent = value;
26
- }
27
- else if (key === 'className') {
28
- el.className = value;
29
- }
30
- else {
31
- el.setAttribute(key, value);
32
- }
33
- }
34
- document.body.appendChild(el);
35
- return el;
36
- }
37
- // =============================================================================
38
- // generateSelector
39
- // =============================================================================
40
- describe('generateSelector', () => {
41
- afterEach(() => {
42
- vi.clearAllMocks();
43
- document.body.innerHTML = '';
44
- });
45
- it('calls getCssSelector with element and default options', () => {
46
- const el = createElement('div');
47
- mockGetCssSelector.mockReturnValue('div');
48
- generateSelector(el);
49
- expect(mockGetCssSelector).toHaveBeenCalledWith(el, expect.objectContaining({
50
- includeTag: true,
51
- maxCombinations: 100,
52
- }));
53
- });
54
- it('returns the selector from getCssSelector', () => {
55
- const el = createElement('div', { id: 'my-id' });
56
- mockGetCssSelector.mockReturnValue('#my-id');
57
- const result = generateSelector(el);
58
- expect(result).toBe('#my-id');
59
- });
60
- it('passes custom options through', () => {
61
- const el = createElement('div');
62
- mockGetCssSelector.mockReturnValue('div');
63
- generateSelector(el, { includeTag: false, maxCombinations: 50 });
64
- expect(mockGetCssSelector).toHaveBeenCalledWith(el, expect.objectContaining({
65
- includeTag: false,
66
- maxCombinations: 50,
67
- }));
68
- });
69
- it('includes attribute selectors when preferTestIds is true (default)', () => {
70
- const el = createElement('div');
71
- mockGetCssSelector.mockReturnValue('div');
72
- generateSelector(el);
73
- const call = mockGetCssSelector.mock.calls[0];
74
- const options = call[1];
75
- expect(options.selectors).toContain('attribute');
76
- });
77
- it('excludes attribute selectors when preferTestIds is false', () => {
78
- const el = createElement('div');
79
- mockGetCssSelector.mockReturnValue('div');
80
- generateSelector(el, { preferTestIds: false });
81
- const call = mockGetCssSelector.mock.calls[0];
82
- const options = call[1];
83
- expect(options.selectors).not.toContain('attribute');
84
- });
85
- it('includes blacklist patterns for dynamic classes', () => {
86
- const el = createElement('div');
87
- mockGetCssSelector.mockReturnValue('div');
88
- generateSelector(el);
89
- const call = mockGetCssSelector.mock.calls[0];
90
- const options = call[1];
91
- expect(options.blacklist).toBeDefined();
92
- expect(Array.isArray(options.blacklist)).toBe(true);
93
- expect(options.blacklist.length).toBeGreaterThan(0);
94
- });
95
- it('includes whitelist for stable attributes', () => {
96
- const el = createElement('div');
97
- mockGetCssSelector.mockReturnValue('div');
98
- generateSelector(el);
99
- const call = mockGetCssSelector.mock.calls[0];
100
- const options = call[1];
101
- expect(options.whitelist).toBeDefined();
102
- expect(Array.isArray(options.whitelist)).toBe(true);
103
- });
104
- it('falls back to generateFallbackSelector when getCssSelector throws', () => {
105
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
106
- const parent = document.createElement('div');
107
- parent.id = 'parent';
108
- document.body.appendChild(parent);
109
- const child = document.createElement('span');
110
- parent.appendChild(child);
111
- mockGetCssSelector.mockImplementation(() => {
112
- throw new Error('Failed');
113
- });
114
- const result = generateSelector(child);
115
- // Fallback should produce a path-based selector
116
- expect(result).toContain('#parent');
117
- expect(result).toContain('span');
118
- expect(warnSpy).toHaveBeenCalled();
119
- document.body.removeChild(parent);
120
- });
121
- describe('fallback selector generation', () => {
122
- it('uses ID when element has an ID', () => {
123
- const el = document.createElement('div');
124
- el.id = 'unique-element';
125
- document.body.appendChild(el);
126
- mockGetCssSelector.mockImplementation(() => {
127
- throw new Error('Failed');
128
- });
129
- vi.spyOn(console, 'warn').mockImplementation(() => { });
130
- const result = generateSelector(el);
131
- expect(result).toContain('#unique-element');
132
- document.body.removeChild(el);
133
- });
134
- it('uses nth-child for elements without ID', () => {
135
- const parent = document.createElement('div');
136
- document.body.appendChild(parent);
137
- const child1 = document.createElement('p');
138
- const child2 = document.createElement('p');
139
- parent.appendChild(child1);
140
- parent.appendChild(child2);
141
- mockGetCssSelector.mockImplementation(() => {
142
- throw new Error('Failed');
143
- });
144
- vi.spyOn(console, 'warn').mockImplementation(() => { });
145
- const result = generateSelector(child2);
146
- expect(result).toContain('nth-child');
147
- document.body.removeChild(parent);
148
- });
149
- it('stops at element with ID (shortest unique path)', () => {
150
- const grandparent = document.createElement('div');
151
- grandparent.id = 'root';
152
- document.body.appendChild(grandparent);
153
- const parent = document.createElement('div');
154
- grandparent.appendChild(parent);
155
- const child = document.createElement('span');
156
- parent.appendChild(child);
157
- mockGetCssSelector.mockImplementation(() => {
158
- throw new Error('Failed');
159
- });
160
- vi.spyOn(console, 'warn').mockImplementation(() => { });
161
- const result = generateSelector(child);
162
- // Should start with #root
163
- expect(result.startsWith('#root')).toBe(true);
164
- document.body.removeChild(grandparent);
165
- });
166
- });
167
- });
168
- // =============================================================================
169
- // validateSelector
170
- // =============================================================================
171
- describe('validateSelector', () => {
172
- afterEach(() => {
173
- document.body.innerHTML = '';
174
- });
175
- it('returns true when selector matches the expected element', () => {
176
- const el = createElement('div', { id: 'target' });
177
- expect(validateSelector('#target', el)).toBe(true);
178
- });
179
- it('returns false when selector matches a different element', () => {
180
- const el1 = createElement('div', { id: 'first' });
181
- createElement('div', { id: 'second' });
182
- expect(validateSelector('#second', el1)).toBe(false);
183
- });
184
- it('returns false when selector matches nothing', () => {
185
- const el = createElement('div');
186
- expect(validateSelector('#nonexistent', el)).toBe(false);
187
- });
188
- it('returns false for invalid selector syntax', () => {
189
- const el = createElement('div');
190
- expect(validateSelector('[[[invalid', el)).toBe(false);
191
- });
192
- });
193
- // =============================================================================
194
- // getElementDescription
195
- // =============================================================================
196
- describe('getElementDescription', () => {
197
- afterEach(() => {
198
- document.body.innerHTML = '';
199
- });
200
- it('includes tag name', () => {
201
- const el = createElement('button');
202
- expect(getElementDescription(el)).toContain('button');
203
- });
204
- it('includes ID when present', () => {
205
- const el = createElement('div', { id: 'my-btn' });
206
- expect(getElementDescription(el)).toContain('#my-btn');
207
- });
208
- it('includes static class names', () => {
209
- const el = createElement('div', { className: 'header main-content' });
210
- const desc = getElementDescription(el);
211
- expect(desc).toContain('.header');
212
- expect(desc).toContain('.main-content');
213
- });
214
- it('excludes dynamic class names', () => {
215
- const el = createElement('div', { className: 'css-1abc23 my-class' });
216
- const desc = getElementDescription(el);
217
- // Dynamic class css-1abc23 should be filtered out
218
- expect(desc).not.toContain('css-1abc23');
219
- expect(desc).toContain('my-class');
220
- });
221
- it('includes text content preview (truncated at 30 chars)', () => {
222
- const el = createElement('p', {
223
- textContent: 'This is a very long text that should be truncated',
224
- });
225
- const desc = getElementDescription(el);
226
- expect(desc).toContain('"');
227
- expect(desc).toContain('...');
228
- });
229
- it('includes full text for short content', () => {
230
- const el = createElement('span', { textContent: 'Hello' });
231
- const desc = getElementDescription(el);
232
- expect(desc).toContain('"Hello"');
233
- expect(desc).not.toContain('...');
234
- });
235
- it('handles elements with no text content', () => {
236
- const el = createElement('img');
237
- const desc = getElementDescription(el);
238
- expect(desc).toBe('img');
239
- });
240
- it('limits class names to 2', () => {
241
- const el = createElement('div', { className: 'cls-a cls-b cls-c cls-d' });
242
- const desc = getElementDescription(el);
243
- // Should include at most 2 classes
244
- expect(desc).toContain('.cls-a');
245
- expect(desc).toContain('.cls-b');
246
- expect(desc).not.toContain('.cls-c');
247
- });
248
- it('handles SVGElement className (not a string)', () => {
249
- const svgNS = 'http://www.w3.org/2000/svg';
250
- const svg = document.createElementNS(svgNS, 'svg');
251
- document.body.appendChild(svg);
252
- // SVGElement.className is an SVGAnimatedString, not a string
253
- const desc = getElementDescription(svg);
254
- expect(desc).toContain('svg');
255
- document.body.removeChild(svg);
256
- });
257
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=useElementRect.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useElementRect.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/useElementRect.test.ts"],"names":[],"mappings":""}