lib-e2e-cypress-for-dummys-ts 0.6.1 → 0.9.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.
package/README.md CHANGED
@@ -123,13 +123,38 @@ The recorder automatically captures:
123
123
  | Interaction | Generated Cypress command |
124
124
  |---|---|
125
125
  | Click on a button or element | `cy.get('[data-cy="submit"]').click()` |
126
+ | Double-click | `cy.get('[data-cy="row"]').dblclick()` |
127
+ | Right-click | `cy.get('[data-cy="ctx"]').rightclick()` |
128
+ | Check / uncheck a checkbox | `cy.get('[data-cy="agree"]').check()` / `.uncheck()` |
129
+ | Pick a radio | `cy.get('[data-cy="plan"]').check()` |
126
130
  | Type into a text field | `cy.get('[data-cy="email"]').clear().type('user@example.com')` |
131
+ | Press Enter / Escape in a field | `cy.get('[data-cy="q"]').type('{enter}')` / `type('{esc}')` |
127
132
  | Select a `<select>` value | `cy.get('[data-cy="country"]').select('ES')` |
128
133
  | SPA route change (push/replace/popstate) | `cy.url().should('include', '/dashboard')` |
129
134
  | Page load | `cy.visit('/current-path')` |
130
135
 
131
136
  Input events are **debounced by 1 second** so only the final value is captured, not every keystroke.
132
137
 
138
+ #### Capturing assertions while recording — Alt+click
139
+
140
+ A test that only clicks around but never checks anything is weak. While recording,
141
+ hold **Alt** and click any element to record a **Cypress assertion** for it instead
142
+ of a click — and the element's real action is **suppressed** (no navigation, no form
143
+ submit), so asserting never changes your flow.
144
+
145
+ The assertion is chosen automatically from the element:
146
+
147
+ | Element | Generated assertion |
148
+ |---|---|
149
+ | Checkbox / radio | `.should('be.checked')` / `.should('not.be.checked')` |
150
+ | Input / textarea / select with a value | `.should('have.value', '<value>')` |
151
+ | Element with visible text | `.should('contain.text', '<text>')` |
152
+ | Anything else | `.should('be.visible')` |
153
+
154
+ The captured assertion appears in the **⌨️ Commands** previewer like any other
155
+ command, so you can reorder or delete it. (You can still build assertions manually
156
+ with the assertion builder — see below.)
157
+
133
158
  ---
134
159
 
135
160
  ### HTTP monitoring
@@ -169,6 +194,23 @@ cy.wait('@post-api-users').then((interception) => {
169
194
 
170
195
  Fields named `id` or `uid` are always skipped (they change between environments). Array responses fall back to the default empty `.then()` block.
171
196
 
197
+ #### Fixture mode (deterministic stubs)
198
+
199
+ Enable **🧪 Fixtures HTTP** in the settings panel to turn GET responses into
200
+ **Cypress fixtures + stubs** instead of spies. While recording, each GET with a
201
+ JSON body is saved as `cypress/fixtures/<alias>.json` and its interceptor becomes:
202
+
203
+ ```javascript
204
+ cy.intercept('GET', '**/api/users', { fixture: 'get-api-users.json' }).as('get-api-users')
205
+ cy.wait('@get-api-users').then((interception) => { })
206
+ ```
207
+
208
+ so the test replays deterministic data with **no backend**. When you save the
209
+ test, the fixture files are written into your `cypress/fixtures/` folder
210
+ automatically (requires the Cypress folder configured — see the advanced editor
211
+ setup). POST/PUT keep the spy + body-validation behaviour; non-JSON GETs fall back
212
+ to a spy.
213
+
172
214
  ---
173
215
 
174
216
  ### Selector strategy
@@ -378,6 +420,15 @@ recorder.resetWidgetPosition();
378
420
 
379
421
  ---
380
422
 
423
+ ### In-app help panel
424
+
425
+ Not sure what the widget can do? Click the **?** button in the widget (or press
426
+ **Ctrl+Shift+H**) to open a categorised cheat-sheet — shortcuts, what gets
427
+ recorded, assertions, the panels, selectors, HTTP, data, and the advanced
428
+ features — in your language (es/en/fr/it/de).
429
+
430
+ ---
431
+
381
432
  ### Keyboard shortcuts
382
433
 
383
434
  | Shortcut | Action |
@@ -388,6 +439,8 @@ recorder.resetWidgetPosition();
388
439
  | `Ctrl + 1` | Open saved tests panel |
389
440
  | `Ctrl + 2` | Open command previewer |
390
441
  | `Ctrl + 3` | Open settings |
442
+ | `Ctrl + Shift + H` | Open the in-app **help panel** |
443
+ | `Alt + click` | Capture an assertion for the clicked element |
391
444
 
392
445
  > `Ctrl + R` / `Ctrl + P` and the panel shortcuts only fire when the widget is **visible**. `Ctrl + Shift + E` always works regardless of visibility state.
393
446
 
@@ -394,4 +394,4 @@ export {
394
394
  escAttr,
395
395
  SelectorPickerElement
396
396
  };
397
- //# sourceMappingURL=chunk-FDMUIHDZ.js.map
397
+ //# sourceMappingURL=chunk-CQEDHL7A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/selector-quality.utils.ts","../src/components/selector-picker/selector-picker.styles.ts","../src/utils/html.utils.ts","../src/components/selector-picker/selector-picker.template.ts","../src/components/selector-picker/selector-picker.ts"],"sourcesContent":["export type SelectorQuality = 'excellent' | 'good' | 'acceptable' | 'poor';\r\n\r\nexport const FORBIDDEN_ID_PREFIXES = [\r\n 'cdk-', 'mat-', 'p-', 'ng-', 'mdc-',\r\n 'primeng-', 'auto-', 'field-', 'input-', 'select-',\r\n];\r\n\r\nexport function getSelectorQuality(el: HTMLElement): SelectorQuality {\r\n if (\r\n el.hasAttribute('data-cy') ||\r\n el.hasAttribute('data-testid') ||\r\n el.hasAttribute('aria-label')\r\n ) {\r\n return 'excellent';\r\n }\r\n\r\n const rawId = el.id;\r\n if (\r\n rawId &&\r\n rawId.length < 25 &&\r\n /^[a-zA-Z][\\w-]*$/.test(rawId) &&\r\n !FORBIDDEN_ID_PREFIXES.some((p) => rawId.startsWith(p)) &&\r\n !/^\\d+$/.test(rawId)\r\n ) {\r\n return 'good';\r\n }\r\n\r\n if (typeof el.className === 'string' && el.className.trim().length > 0) {\r\n return 'acceptable';\r\n }\r\n\r\n return 'poor';\r\n}\r\n\r\n/** Returns the best cy.get() selector string for a given element. */\r\nexport function buildPickerSelector(el: HTMLElement): string {\r\n const dataCy = el.getAttribute('data-cy');\r\n if (dataCy) return `[data-cy=\"${dataCy}\"]`;\r\n\r\n const dataTestid = el.getAttribute('data-testid');\r\n if (dataTestid) return `[data-testid=\"${dataTestid}\"]`;\r\n\r\n const ariaLabel = el.getAttribute('aria-label');\r\n if (ariaLabel) return `[aria-label=\"${ariaLabel}\"]`;\r\n\r\n const rawId = el.id;\r\n if (\r\n rawId &&\r\n rawId.length < 25 &&\r\n /^[a-zA-Z][\\w-]*$/.test(rawId) &&\r\n !FORBIDDEN_ID_PREFIXES.some((p) => rawId.startsWith(p)) &&\r\n !/^\\d+$/.test(rawId)\r\n ) {\r\n return `#${rawId}`;\r\n }\r\n\r\n if (typeof el.className === 'string' && el.className.trim().length > 0) {\r\n const classes = el.className.trim().split(/\\s+/);\r\n return classes.map((c) => `.${c}`).join('');\r\n }\r\n\r\n return el.tagName.toLowerCase();\r\n}\r\n\r\n/** Human-readable key attribute/selector preview for a picker row (raw, unescaped). */\r\nexport function 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}=\"${v}\"`;\r\n }\r\n if (el.id) return `id=\"${el.id}\"`;\r\n if (el.className && el.className.trim()) {\r\n return el.className.trim().split(/\\s+/).map((c) => `.${c}`).join('');\r\n }\r\n return '';\r\n}\r\n\r\n/** Plain, serializable view-model for one picker row — no DOM held. */\r\nexport interface PickerRow {\r\n quality: SelectorQuality;\r\n selector: string;\r\n tag: string;\r\n attr: string;\r\n}\r\n\r\n/** Reads an element once and returns the plain data a picker row needs to render. */\r\nexport function describePickerRow(el: HTMLElement): PickerRow {\r\n return {\r\n quality: getSelectorQuality(el),\r\n selector: buildPickerSelector(el),\r\n tag: el.tagName.toLowerCase(),\r\n attr: keyAttrDisplay(el),\r\n };\r\n}\r\n","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","export function escHtml(s: string): string {\r\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\r\n}\r\n\r\nexport function escAttr(s: string): string {\r\n return s.replace(/\"/g, '&quot;').replace(/</g, '&lt;');\r\n}\r\n","import type { PickerRow, 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\nexport function renderPickerRows(\r\n rows: PickerRow[],\r\n selectedIndex: number,\r\n t: (key: string) => string,\r\n): string {\r\n return rows.map((row, i) => {\r\n const isSelected = i === selectedIndex;\r\n const isPoor = row.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[row.quality]}\" title=\"${escHtml(t(`SELECTOR_PICKER.QUALITY_${row.quality.toUpperCase()}`))}\">\r\n ${QUALITY_EMOJI[row.quality]}\r\n </span>\r\n <span class=\"tag-name\">&lt;${escHtml(row.tag)}&gt;</span>\r\n ${row.attr ? `<span class=\"attr-value\">${escHtml(row.attr)}</span>` : '<span class=\"attr-value\"></span>'}\r\n <span class=\"selector-preview\">${escHtml(row.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 rows: PickerRow[],\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(rows, 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 { describePickerRow, type PickerRow } 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 rows: PickerRow[] = [];\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.rows = this.ancestors.map(describePickerRow);\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.rows.length; i++) {\r\n const q = this.rows[i].quality;\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.rows,\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 row = this.rows[index];\r\n if (!row) return;\r\n const command = `cy.get('${row.selector}').click()`;\r\n this.recording.appendCommand(command);\r\n this.dispatchEvent(new CustomEvent('selectorchosen', { detail: command, bubbles: true, composed: true }));\r\n this.closeSilently();\r\n }\r\n\r\n private cancel(): void {\r\n this.dispatchEvent(new CustomEvent('pickercancelled', { bubbles: true, composed: 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":[]}