lib-e2e-cypress-for-dummys-ts 0.1.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.
@@ -0,0 +1,340 @@
1
+ import {
2
+ buildPickerSelector,
3
+ escHtml,
4
+ getSelectorQuality
5
+ } from "./chunk-DRNRKHXN.js";
6
+
7
+ // src/components/selector-picker/selector-picker.styles.ts
8
+ var SELECTOR_PICKER_STYLES = `
9
+ :host {
10
+ position: fixed;
11
+ z-index: 2147483647;
12
+ top: 0;
13
+ left: 0;
14
+ width: 100vw;
15
+ height: 100vh;
16
+ pointer-events: none;
17
+ }
18
+
19
+ .overlay {
20
+ position: absolute;
21
+ top: 50%;
22
+ left: 50%;
23
+ transform: translate(-50%, -50%);
24
+ pointer-events: all;
25
+ background: #161b22;
26
+ border: 1px solid #30363d;
27
+ border-radius: 10px;
28
+ box-shadow: 0 8px 32px rgba(0,0,0,0.6);
29
+ min-width: 420px;
30
+ max-width: 600px;
31
+ font-family: 'Segoe UI', system-ui, sans-serif;
32
+ color: #e6edf3;
33
+ font-size: 13px;
34
+ }
35
+
36
+ .header {
37
+ padding: 12px 16px 8px;
38
+ border-bottom: 1px solid #21262d;
39
+ font-weight: 600;
40
+ font-size: 13px;
41
+ color: #8b949e;
42
+ letter-spacing: 0.04em;
43
+ text-transform: uppercase;
44
+ }
45
+
46
+ .list {
47
+ max-height: 320px;
48
+ overflow-y: auto;
49
+ padding: 6px 0;
50
+ }
51
+
52
+ .row {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 8px;
56
+ padding: 7px 14px;
57
+ cursor: pointer;
58
+ border-radius: 6px;
59
+ margin: 1px 6px;
60
+ transition: background 0.1s;
61
+ }
62
+
63
+ .row:hover,
64
+ .row.selected {
65
+ background: #21262d;
66
+ }
67
+
68
+ .row.selected {
69
+ outline: 2px solid #388bfd;
70
+ outline-offset: -2px;
71
+ }
72
+
73
+ .quality-badge {
74
+ width: 18px;
75
+ height: 18px;
76
+ border-radius: 50%;
77
+ flex-shrink: 0;
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ font-size: 10px;
82
+ }
83
+
84
+ .badge-excellent { background: #238636; }
85
+ .badge-good { background: #1f6feb; }
86
+ .badge-acceptable{ background: #9e6a03; }
87
+ .badge-poor { background: #da3633; }
88
+
89
+ .tag-name {
90
+ font-family: monospace;
91
+ font-size: 12px;
92
+ color: #8b949e;
93
+ flex-shrink: 0;
94
+ }
95
+
96
+ .attr-value {
97
+ font-family: monospace;
98
+ font-size: 12px;
99
+ color: #c9d1d9;
100
+ flex: 1;
101
+ overflow: hidden;
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ }
105
+
106
+ .selector-preview {
107
+ font-family: monospace;
108
+ font-size: 11px;
109
+ color: #58a6ff;
110
+ flex-shrink: 0;
111
+ max-width: 180px;
112
+ overflow: hidden;
113
+ text-overflow: ellipsis;
114
+ white-space: nowrap;
115
+ }
116
+
117
+ .poor-warning {
118
+ font-size: 10px;
119
+ color: #f85149;
120
+ margin-left: auto;
121
+ }
122
+
123
+ .footer {
124
+ padding: 8px 14px 10px;
125
+ border-top: 1px solid #21262d;
126
+ display: flex;
127
+ gap: 16px;
128
+ font-size: 11px;
129
+ color: #484f58;
130
+ }
131
+
132
+ .hint kbd {
133
+ background: #21262d;
134
+ border: 1px solid #30363d;
135
+ border-radius: 3px;
136
+ padding: 1px 5px;
137
+ font-size: 10px;
138
+ color: #8b949e;
139
+ }
140
+ `;
141
+
142
+ // src/components/selector-picker/selector-picker.template.ts
143
+ var QUALITY_EMOJI = {
144
+ excellent: "\u{1F7E2}",
145
+ good: "\u{1F535}",
146
+ acceptable: "\u{1F7E1}",
147
+ poor: "\u{1F534}"
148
+ };
149
+ var QUALITY_CLASS = {
150
+ excellent: "badge-excellent",
151
+ good: "badge-good",
152
+ acceptable: "badge-acceptable",
153
+ poor: "badge-poor"
154
+ };
155
+ function keyAttrDisplay(el) {
156
+ for (const attr of ["data-cy", "data-testid", "aria-label"]) {
157
+ const v = el.getAttribute(attr);
158
+ if (v) return `${attr}="${escHtml(v)}"`;
159
+ }
160
+ if (el.id) return `id="${escHtml(el.id)}"`;
161
+ if (el.className && el.className.trim()) return escHtml(el.className.trim().split(/\s+/).map((c) => `.${c}`).join(""));
162
+ return "";
163
+ }
164
+ function renderPickerRows(ancestors, selectedIndex, t) {
165
+ return ancestors.map((el, i) => {
166
+ const quality = getSelectorQuality(el);
167
+ const selector = buildPickerSelector(el);
168
+ const tag = el.tagName.toLowerCase();
169
+ const attr = keyAttrDisplay(el);
170
+ const isSelected = i === selectedIndex;
171
+ const isPoor = quality === "poor";
172
+ return `
173
+ <div class="row${isSelected ? " selected" : ""}" data-idx="${i}" role="option" aria-selected="${isSelected}">
174
+ <span class="quality-badge ${QUALITY_CLASS[quality]}" title="${escHtml(t(`SELECTOR_PICKER.QUALITY_${quality.toUpperCase()}`))}">
175
+ ${QUALITY_EMOJI[quality]}
176
+ </span>
177
+ <span class="tag-name">&lt;${escHtml(tag)}&gt;</span>
178
+ ${attr ? `<span class="attr-value">${attr}</span>` : '<span class="attr-value"></span>'}
179
+ <span class="selector-preview">${escHtml(selector)}</span>
180
+ ${isPoor ? `<span class="poor-warning" title="${escHtml(t("SELECTOR_PICKER.QUALITY_POOR"))}">\u26A0</span>` : ""}
181
+ </div>`;
182
+ }).join("");
183
+ }
184
+ function renderPicker(ancestors, selectedIndex, t) {
185
+ return `
186
+ <div class="overlay" role="listbox">
187
+ <div class="header">${escHtml(t("SELECTOR_PICKER.TITLE"))}</div>
188
+ <div class="list">
189
+ ${renderPickerRows(ancestors, selectedIndex, t)}
190
+ </div>
191
+ <div class="footer">
192
+ <span class="hint"><kbd>\u2191\u2193</kbd> ${escHtml(t("SELECTOR_PICKER.PREVIEW_LABEL"))}</span>
193
+ <span class="hint"><kbd>Enter</kbd> ${escHtml(t("SELECTOR_PICKER.CONFIRM_HINT"))}</span>
194
+ <span class="hint"><kbd>Esc</kbd> ${escHtml(t("SELECTOR_PICKER.CANCEL_HINT"))}</span>
195
+ </div>
196
+ </div>`;
197
+ }
198
+
199
+ // src/components/selector-picker/selector-picker.ts
200
+ var OWN_DATA_CY = "lib-e2e-cypress-for-dummys";
201
+ var MAX_ANCESTORS = 10;
202
+ var SelectorPickerElement = class extends HTMLElement {
203
+ targetElement = null;
204
+ recording;
205
+ translation;
206
+ shadow;
207
+ ancestors = [];
208
+ selectedIndex = 0;
209
+ keyHandler = null;
210
+ unsubRecording = null;
211
+ unsubPause = null;
212
+ constructor() {
213
+ super();
214
+ this.shadow = this.attachShadow({ mode: "open" });
215
+ }
216
+ connectedCallback() {
217
+ this.setAttribute("data-cy", "lib-e2e-cypress-for-dummys");
218
+ this.ancestors = this.buildAncestors();
219
+ this.selectedIndex = this.findBestIndex();
220
+ this.render();
221
+ this.attachKeyListener();
222
+ this.attachRowClickListeners();
223
+ this.subscribeToRecordingChanges();
224
+ }
225
+ disconnectedCallback() {
226
+ this.detachKeyListener();
227
+ this.unsubRecording?.();
228
+ this.unsubPause?.();
229
+ }
230
+ t(key) {
231
+ return this.translation?.translate(key) ?? key;
232
+ }
233
+ // ── Ancestor chain ──────────────────────────────────────────────────────────
234
+ buildAncestors() {
235
+ const chain = [];
236
+ let current = this.targetElement;
237
+ while (current && current.tagName.toLowerCase() !== "html" && chain.length < MAX_ANCESTORS) {
238
+ if (current.getAttribute("data-cy") === OWN_DATA_CY) {
239
+ current = current.parentElement;
240
+ continue;
241
+ }
242
+ chain.push(current);
243
+ current = current.parentElement;
244
+ }
245
+ return chain;
246
+ }
247
+ findBestIndex() {
248
+ for (let i = 0; i < this.ancestors.length; i++) {
249
+ const q = getSelectorQuality(this.ancestors[i]);
250
+ if (q === "excellent" || q === "good" || q === "acceptable") return i;
251
+ }
252
+ return 0;
253
+ }
254
+ // ── Rendering ───────────────────────────────────────────────────────────────
255
+ render() {
256
+ this.shadow.innerHTML = `<style>${SELECTOR_PICKER_STYLES}</style>${renderPicker(
257
+ this.ancestors,
258
+ this.selectedIndex,
259
+ this.t.bind(this)
260
+ )}`;
261
+ }
262
+ // ── Keyboard ────────────────────────────────────────────────────────────────
263
+ attachKeyListener() {
264
+ this.keyHandler = (e) => {
265
+ if (e.key === "ArrowDown") {
266
+ e.stopPropagation();
267
+ e.preventDefault();
268
+ this.selectedIndex = (this.selectedIndex + 1) % this.ancestors.length;
269
+ this.render();
270
+ this.attachRowClickListeners();
271
+ } else if (e.key === "ArrowUp") {
272
+ e.stopPropagation();
273
+ e.preventDefault();
274
+ this.selectedIndex = (this.selectedIndex - 1 + this.ancestors.length) % this.ancestors.length;
275
+ this.render();
276
+ this.attachRowClickListeners();
277
+ } else if (e.key === "Enter") {
278
+ e.stopPropagation();
279
+ this.confirm(this.selectedIndex);
280
+ } else if (e.key === "Escape") {
281
+ e.stopPropagation();
282
+ this.cancel();
283
+ }
284
+ };
285
+ window.addEventListener("keydown", this.keyHandler, { capture: true });
286
+ }
287
+ detachKeyListener() {
288
+ if (this.keyHandler) {
289
+ window.removeEventListener("keydown", this.keyHandler, { capture: true });
290
+ this.keyHandler = null;
291
+ }
292
+ }
293
+ // ── Row click listeners ─────────────────────────────────────────────────────
294
+ attachRowClickListeners() {
295
+ this.shadow.querySelectorAll("[data-idx]").forEach((row) => {
296
+ row.addEventListener("click", () => {
297
+ const idx = parseInt(row.dataset["idx"] ?? "0", 10);
298
+ this.confirm(idx);
299
+ });
300
+ });
301
+ }
302
+ // ── Recording auto-close ────────────────────────────────────────────────────
303
+ subscribeToRecordingChanges() {
304
+ this.unsubRecording = this.recording.onRecordingChange((isRecording) => {
305
+ if (!isRecording) this.closeSilently();
306
+ });
307
+ this.unsubPause = this.recording.onPauseChange((isPaused) => {
308
+ if (isPaused) this.closeSilently();
309
+ });
310
+ }
311
+ // ── Actions ─────────────────────────────────────────────────────────────────
312
+ confirm(index) {
313
+ const el = this.ancestors[index];
314
+ if (!el) return;
315
+ const selector = buildPickerSelector(el);
316
+ const command = `cy.get('${selector}').click()`;
317
+ this.recording.appendCommand(command);
318
+ this.dispatchEvent(new CustomEvent("selectorchosen", { detail: command, bubbles: true }));
319
+ this.closeSilently();
320
+ }
321
+ cancel() {
322
+ this.dispatchEvent(new CustomEvent("pickercancelled", { bubbles: true }));
323
+ this.closeSilently();
324
+ }
325
+ closeSilently() {
326
+ this.detachKeyListener();
327
+ this.unsubRecording?.();
328
+ this.unsubPause?.();
329
+ this.unsubRecording = null;
330
+ this.unsubPause = null;
331
+ this.remove();
332
+ }
333
+ };
334
+ if (!customElements.get("selector-picker")) {
335
+ customElements.define("selector-picker", SelectorPickerElement);
336
+ }
337
+ export {
338
+ SelectorPickerElement
339
+ };
340
+ //# sourceMappingURL=selector-picker-VJOLGZ5H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/selector-picker/selector-picker.styles.ts","../src/components/selector-picker/selector-picker.template.ts","../src/components/selector-picker/selector-picker.ts"],"sourcesContent":["export const SELECTOR_PICKER_STYLES = `\r\n :host {\r\n position: fixed;\r\n z-index: 2147483647;\r\n top: 0;\r\n left: 0;\r\n width: 100vw;\r\n height: 100vh;\r\n pointer-events: none;\r\n }\r\n\r\n .overlay {\r\n position: absolute;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n pointer-events: all;\r\n background: #161b22;\r\n border: 1px solid #30363d;\r\n border-radius: 10px;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.6);\r\n min-width: 420px;\r\n max-width: 600px;\r\n font-family: 'Segoe UI', system-ui, sans-serif;\r\n color: #e6edf3;\r\n font-size: 13px;\r\n }\r\n\r\n .header {\r\n padding: 12px 16px 8px;\r\n border-bottom: 1px solid #21262d;\r\n font-weight: 600;\r\n font-size: 13px;\r\n color: #8b949e;\r\n letter-spacing: 0.04em;\r\n text-transform: uppercase;\r\n }\r\n\r\n .list {\r\n max-height: 320px;\r\n overflow-y: auto;\r\n padding: 6px 0;\r\n }\r\n\r\n .row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 7px 14px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n margin: 1px 6px;\r\n transition: background 0.1s;\r\n }\r\n\r\n .row:hover,\r\n .row.selected {\r\n background: #21262d;\r\n }\r\n\r\n .row.selected {\r\n outline: 2px solid #388bfd;\r\n outline-offset: -2px;\r\n }\r\n\r\n .quality-badge {\r\n width: 18px;\r\n height: 18px;\r\n border-radius: 50%;\r\n flex-shrink: 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 10px;\r\n }\r\n\r\n .badge-excellent { background: #238636; }\r\n .badge-good { background: #1f6feb; }\r\n .badge-acceptable{ background: #9e6a03; }\r\n .badge-poor { background: #da3633; }\r\n\r\n .tag-name {\r\n font-family: monospace;\r\n font-size: 12px;\r\n color: #8b949e;\r\n flex-shrink: 0;\r\n }\r\n\r\n .attr-value {\r\n font-family: monospace;\r\n font-size: 12px;\r\n color: #c9d1d9;\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n\r\n .selector-preview {\r\n font-family: monospace;\r\n font-size: 11px;\r\n color: #58a6ff;\r\n flex-shrink: 0;\r\n max-width: 180px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n\r\n .poor-warning {\r\n font-size: 10px;\r\n color: #f85149;\r\n margin-left: auto;\r\n }\r\n\r\n .footer {\r\n padding: 8px 14px 10px;\r\n border-top: 1px solid #21262d;\r\n display: flex;\r\n gap: 16px;\r\n font-size: 11px;\r\n color: #484f58;\r\n }\r\n\r\n .hint kbd {\r\n background: #21262d;\r\n border: 1px solid #30363d;\r\n border-radius: 3px;\r\n padding: 1px 5px;\r\n font-size: 10px;\r\n color: #8b949e;\r\n }\r\n`;\r\n","import { getSelectorQuality, buildPickerSelector, type SelectorQuality } from '../../utils/selector-quality.utils';\r\nimport { escHtml } from '../../utils/html.utils';\r\n\r\nconst QUALITY_EMOJI: Record<SelectorQuality, string> = {\r\n excellent: '🟢',\r\n good: '🔵',\r\n acceptable: '🟡',\r\n poor: '🔴',\r\n};\r\n\r\nconst QUALITY_CLASS: Record<SelectorQuality, string> = {\r\n excellent: 'badge-excellent',\r\n good: 'badge-good',\r\n acceptable: 'badge-acceptable',\r\n poor: 'badge-poor',\r\n};\r\n\r\nfunction keyAttrDisplay(el: HTMLElement): string {\r\n for (const attr of ['data-cy', 'data-testid', 'aria-label']) {\r\n const v = el.getAttribute(attr);\r\n if (v) return `${attr}=\"${escHtml(v)}\"`;\r\n }\r\n if (el.id) return `id=\"${escHtml(el.id)}\"`;\r\n if (el.className && el.className.trim()) return escHtml(el.className.trim().split(/\\s+/).map((c) => `.${c}`).join(''));\r\n return '';\r\n}\r\n\r\nexport function renderPickerRows(\r\n ancestors: HTMLElement[],\r\n selectedIndex: number,\r\n t: (key: string) => string,\r\n): string {\r\n return ancestors.map((el, i) => {\r\n const quality = getSelectorQuality(el);\r\n const selector = buildPickerSelector(el);\r\n const tag = el.tagName.toLowerCase();\r\n const attr = keyAttrDisplay(el);\r\n const isSelected = i === selectedIndex;\r\n const isPoor = quality === 'poor';\r\n\r\n return `\r\n <div class=\"row${isSelected ? ' selected' : ''}\" data-idx=\"${i}\" role=\"option\" aria-selected=\"${isSelected}\">\r\n <span class=\"quality-badge ${QUALITY_CLASS[quality]}\" title=\"${escHtml(t(`SELECTOR_PICKER.QUALITY_${quality.toUpperCase()}`))}\">\r\n ${QUALITY_EMOJI[quality]}\r\n </span>\r\n <span class=\"tag-name\">&lt;${escHtml(tag)}&gt;</span>\r\n ${attr ? `<span class=\"attr-value\">${attr}</span>` : '<span class=\"attr-value\"></span>'}\r\n <span class=\"selector-preview\">${escHtml(selector)}</span>\r\n ${isPoor ? `<span class=\"poor-warning\" title=\"${escHtml(t('SELECTOR_PICKER.QUALITY_POOR'))}\">⚠</span>` : ''}\r\n </div>`;\r\n }).join('');\r\n}\r\n\r\nexport function renderPicker(\r\n ancestors: HTMLElement[],\r\n selectedIndex: number,\r\n t: (key: string) => string,\r\n): string {\r\n return `\r\n <div class=\"overlay\" role=\"listbox\">\r\n <div class=\"header\">${escHtml(t('SELECTOR_PICKER.TITLE'))}</div>\r\n <div class=\"list\">\r\n ${renderPickerRows(ancestors, selectedIndex, t)}\r\n </div>\r\n <div class=\"footer\">\r\n <span class=\"hint\"><kbd>↑↓</kbd> ${escHtml(t('SELECTOR_PICKER.PREVIEW_LABEL'))}</span>\r\n <span class=\"hint\"><kbd>Enter</kbd> ${escHtml(t('SELECTOR_PICKER.CONFIRM_HINT'))}</span>\r\n <span class=\"hint\"><kbd>Esc</kbd> ${escHtml(t('SELECTOR_PICKER.CANCEL_HINT'))}</span>\r\n </div>\r\n </div>`;\r\n}\r\n","import type { RecordingService } from '../../services/recording.service';\r\nimport type { TranslationService } from '../../services/translation.service';\r\nimport { getSelectorQuality, buildPickerSelector } from '../../utils/selector-quality.utils';\r\nimport { SELECTOR_PICKER_STYLES } from './selector-picker.styles';\r\nimport { renderPicker } from './selector-picker.template';\r\n\r\nconst OWN_DATA_CY = 'lib-e2e-cypress-for-dummys';\r\nconst MAX_ANCESTORS = 10;\r\n\r\nexport class SelectorPickerElement extends HTMLElement {\r\n targetElement: HTMLElement | null = null;\r\n recording!: RecordingService;\r\n translation!: TranslationService;\r\n\r\n private shadow: ShadowRoot;\r\n private ancestors: HTMLElement[] = [];\r\n private selectedIndex = 0;\r\n private keyHandler: ((e: KeyboardEvent) => void) | null = null;\r\n private unsubRecording: (() => void) | null = null;\r\n private unsubPause: (() => void) | null = null;\r\n\r\n constructor() {\r\n super();\r\n this.shadow = this.attachShadow({ mode: 'open' });\r\n }\r\n\r\n connectedCallback(): void {\r\n this.setAttribute('data-cy', 'lib-e2e-cypress-for-dummys');\r\n this.ancestors = this.buildAncestors();\r\n this.selectedIndex = this.findBestIndex();\r\n this.render();\r\n this.attachKeyListener();\r\n this.attachRowClickListeners();\r\n this.subscribeToRecordingChanges();\r\n }\r\n\r\n disconnectedCallback(): void {\r\n this.detachKeyListener();\r\n this.unsubRecording?.();\r\n this.unsubPause?.();\r\n }\r\n\r\n private t(key: string): string {\r\n return this.translation?.translate(key) ?? key;\r\n }\r\n\r\n // ── Ancestor chain ──────────────────────────────────────────────────────────\r\n\r\n private buildAncestors(): HTMLElement[] {\r\n const chain: HTMLElement[] = [];\r\n let current: HTMLElement | null = this.targetElement;\r\n\r\n while (current && current.tagName.toLowerCase() !== 'html' && chain.length < MAX_ANCESTORS) {\r\n if (current.getAttribute('data-cy') === OWN_DATA_CY) {\r\n current = current.parentElement;\r\n continue;\r\n }\r\n chain.push(current);\r\n current = current.parentElement;\r\n }\r\n\r\n return chain;\r\n }\r\n\r\n private findBestIndex(): number {\r\n for (let i = 0; i < this.ancestors.length; i++) {\r\n const q = getSelectorQuality(this.ancestors[i]);\r\n if (q === 'excellent' || q === 'good' || q === 'acceptable') return i;\r\n }\r\n return 0;\r\n }\r\n\r\n // ── Rendering ───────────────────────────────────────────────────────────────\r\n\r\n private render(): void {\r\n this.shadow.innerHTML = `<style>${SELECTOR_PICKER_STYLES}</style>${renderPicker(\r\n this.ancestors,\r\n this.selectedIndex,\r\n this.t.bind(this),\r\n )}`;\r\n }\r\n\r\n // ── Keyboard ────────────────────────────────────────────────────────────────\r\n\r\n private attachKeyListener(): void {\r\n this.keyHandler = (e: KeyboardEvent) => {\r\n if (e.key === 'ArrowDown') {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n this.selectedIndex = (this.selectedIndex + 1) % this.ancestors.length;\r\n this.render();\r\n this.attachRowClickListeners();\r\n } else if (e.key === 'ArrowUp') {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n this.selectedIndex = (this.selectedIndex - 1 + this.ancestors.length) % this.ancestors.length;\r\n this.render();\r\n this.attachRowClickListeners();\r\n } else if (e.key === 'Enter') {\r\n e.stopPropagation();\r\n this.confirm(this.selectedIndex);\r\n } else if (e.key === 'Escape') {\r\n e.stopPropagation();\r\n this.cancel();\r\n }\r\n };\r\n window.addEventListener('keydown', this.keyHandler, { capture: true });\r\n }\r\n\r\n private detachKeyListener(): void {\r\n if (this.keyHandler) {\r\n window.removeEventListener('keydown', this.keyHandler, { capture: true });\r\n this.keyHandler = null;\r\n }\r\n }\r\n\r\n // ── Row click listeners ─────────────────────────────────────────────────────\r\n\r\n private attachRowClickListeners(): void {\r\n this.shadow.querySelectorAll('[data-idx]').forEach((row) => {\r\n (row as HTMLElement).addEventListener('click', () => {\r\n const idx = parseInt((row as HTMLElement).dataset['idx'] ?? '0', 10);\r\n this.confirm(idx);\r\n });\r\n });\r\n }\r\n\r\n // ── Recording auto-close ────────────────────────────────────────────────────\r\n\r\n private subscribeToRecordingChanges(): void {\r\n this.unsubRecording = this.recording.onRecordingChange((isRecording) => {\r\n if (!isRecording) this.closeSilently();\r\n });\r\n this.unsubPause = this.recording.onPauseChange((isPaused) => {\r\n if (isPaused) this.closeSilently();\r\n });\r\n }\r\n\r\n // ── Actions ─────────────────────────────────────────────────────────────────\r\n\r\n private confirm(index: number): void {\r\n const el = this.ancestors[index];\r\n if (!el) return;\r\n const selector = buildPickerSelector(el);\r\n const command = `cy.get('${selector}').click()`;\r\n this.recording.appendCommand(command);\r\n this.dispatchEvent(new CustomEvent('selectorchosen', { detail: command, bubbles: true }));\r\n this.closeSilently();\r\n }\r\n\r\n private cancel(): void {\r\n this.dispatchEvent(new CustomEvent('pickercancelled', { bubbles: true }));\r\n this.closeSilently();\r\n }\r\n\r\n private closeSilently(): void {\r\n this.detachKeyListener();\r\n this.unsubRecording?.();\r\n this.unsubPause?.();\r\n this.unsubRecording = null;\r\n this.unsubPause = null;\r\n this.remove();\r\n }\r\n}\r\n\r\nif (!customElements.get('selector-picker')) {\r\n customElements.define('selector-picker', SelectorPickerElement);\r\n}\r\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "lib-e2e-cypress-for-dummys-ts",
3
+ "version": "0.1.0",
4
+ "description": "Cypress E2E test recorder — pure TypeScript, zero Angular dependencies",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "author": "Gonzalo Carmenado <gonzalo.carmenado@gmail.com>",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/GonzaloCarmenado/lib-e2e-cypress-for-dummys-ts.git"
24
+ },
25
+ "homepage": "https://github.com/GonzaloCarmenado/lib-e2e-cypress-for-dummys-ts#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/GonzaloCarmenado/lib-e2e-cypress-for-dummys-ts/issues"
28
+ },
29
+ "keywords": [
30
+ "cypress",
31
+ "e2e",
32
+ "testing",
33
+ "recorder",
34
+ "test-generator",
35
+ "typescript",
36
+ "custom-element",
37
+ "web-component"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "build:watch": "tsup --watch",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "test:coverage": "vitest run --coverage",
45
+ "lint": "eslint",
46
+ "lint:fix": "eslint --fix",
47
+ "prepare": "husky",
48
+ "prepublishOnly": "npm run lint && npm test && npm run build"
49
+ },
50
+ "lint-staged": {
51
+ "*.ts": "eslint --fix"
52
+ },
53
+ "devDependencies": {
54
+ "@eslint/js": "^10.0.1",
55
+ "@types/node": "^22.0.0",
56
+ "@vitest/coverage-v8": "^2.1.0",
57
+ "eslint": "^10.3.0",
58
+ "fake-indexeddb": "^6.2.5",
59
+ "husky": "^9.1.7",
60
+ "jsdom": "^25.0.0",
61
+ "lint-staged": "^17.0.2",
62
+ "tsup": "^8.3.0",
63
+ "typescript": "~5.7.2",
64
+ "typescript-eslint": "^8.59.2",
65
+ "vitest": "^2.1.0"
66
+ },
67
+ "dependencies": {
68
+ "idb": "^8.0.3",
69
+ "sweetalert2": "^11.26.24"
70
+ }
71
+ }