feat(cms): keyboard shortcuts — Ctrl+S save, Ctrl+P publish, ? help
CI Pipeline with Test Management / 🧪 Run Tests & Generate Reports (push) Waiting to run
CI Pipeline with Test Management / 🐳 Docker Integration Tests (push) Blocked by required conditions
CI Pipeline with Test Management / 🏗️ Build Docker Image (push) Blocked by required conditions
CI Pipeline with Test Management / 📊 Generate Test Summary (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🧪 Run Tests & Generate Reports (push) Waiting to run
Test Reporting & Gherkin Analysis / 📊 Analyze Test Coverage (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🔄 Sync with Linear (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / ⚡ Performance Monitoring (push) Blocked by required conditions
CI Pipeline with Test Management / 🧪 Run Tests & Generate Reports (push) Waiting to run
CI Pipeline with Test Management / 🐳 Docker Integration Tests (push) Blocked by required conditions
CI Pipeline with Test Management / 🏗️ Build Docker Image (push) Blocked by required conditions
CI Pipeline with Test Management / 📊 Generate Test Summary (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🧪 Run Tests & Generate Reports (push) Waiting to run
Test Reporting & Gherkin Analysis / 📊 Analyze Test Coverage (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🔄 Sync with Linear (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / ⚡ Performance Monitoring (push) Blocked by required conditions
- Ctrl/Cmd+S saves (native browser save dialog suppressed) - Ctrl/Cmd+P publishes; Ctrl/Cmd+Shift+V opens the Versions panel - '?' toggles a shortcuts overlay (Esc/click closes) - plain typing in inputs never triggers actions (modifiers required; '?' only outside editing targets) - jsdom regression tests run the real client script with dispatched KeyboardEvents; the client is evaluated once per suite because each eval would stack another keydown listener on the shared document Closes MITHOME-75
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Regression tests for the Content Editor keyboard shortcuts (MITHOME-75).
|
||||
* Runs the real scripts/cms-editor-client.js in jsdom, dispatches actual
|
||||
* KeyboardEvents and asserts:
|
||||
* 1. Ctrl+S calls save (fetch /save) and the browser save dialog is
|
||||
* suppressed (preventDefault)
|
||||
* 2. Ctrl+P calls publish
|
||||
* 3. Ctrl+Shift+V opens the versions page in a new tab
|
||||
* 4. '?' opens the shortcuts overlay, Esc closes it
|
||||
* 5. plain 's' inside an input does NOT trigger save
|
||||
*/
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const clientJs = fs.readFileSync(path.join(__dirname, '../../../scripts/cms-editor-client.js'), 'utf8')
|
||||
|
||||
const makeData = () => ({
|
||||
hero: { title: 'T', subtitle: 'S', description: 'D' },
|
||||
})
|
||||
|
||||
function press(target: Document | Element, init: KeyboardEventInit) {
|
||||
const ev = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init })
|
||||
target.dispatchEvent(ev)
|
||||
return ev
|
||||
}
|
||||
|
||||
const flush = () => new Promise(r => setTimeout(r, 0))
|
||||
|
||||
// Globals must exist BEFORE the eval — the client script boots immediately
|
||||
// (render(DATA, …)). The eval runs ONCE: every eval would add another keydown
|
||||
// listener to the shared jsdom document, and the toggle-style overlay handler
|
||||
// would then open/close itself multiple times per keypress.
|
||||
;(global as any).DATA = makeData()
|
||||
;(global as any).FILE = 'home'
|
||||
;(global as any).CSRF_TOKEN = 'boot'
|
||||
;(global as any).CONTENT_HASH = 'x'.repeat(64)
|
||||
;(global as any).fetch = jest.fn(async () => ({ status: 200, ok: true, json: async () => ({ ok: true }) }))
|
||||
;(global as any).window = global
|
||||
document.body.innerHTML = '<div id="editor"></div>'
|
||||
;(0, eval)(clientJs)
|
||||
|
||||
describe('Content Editor keyboard shortcuts', () => {
|
||||
let fetchCalls: Array<{ url: string; init?: RequestInit }>
|
||||
|
||||
beforeEach(() => {
|
||||
fetchCalls = []
|
||||
;(global as any).DATA = makeData()
|
||||
;(global as any).FILE = 'home'
|
||||
;(global as any).CSRF_TOKEN = 'test-csrf'
|
||||
;(global as any).CONTENT_HASH = 'x'.repeat(64)
|
||||
;(global as any).fetch = jest.fn(async (url: string, init?: RequestInit) => {
|
||||
fetchCalls.push({ url, init })
|
||||
return { status: 200, ok: true, json: async () => ({ ok: true, contentHash: 'y'.repeat(64) }) }
|
||||
})
|
||||
;(global as any).window = global
|
||||
;(window as any).open = jest.fn()
|
||||
document.body.innerHTML = '<div id="editor"></div><span id="saveStatus" style="display:none"></span><button id="publishBtn">pub</button>'
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
delete (global as any).DATA
|
||||
delete (global as any).FILE
|
||||
delete (global as any).CSRF_TOKEN
|
||||
delete (global as any).CONTENT_HASH
|
||||
})
|
||||
|
||||
it('Ctrl+S saves via fetch and suppresses the browser save dialog', () => {
|
||||
const ev = press(document, { key: 's', ctrlKey: true })
|
||||
expect(ev.defaultPrevented).toBe(true)
|
||||
expect(fetchCalls.length).toBeGreaterThanOrEqual(1)
|
||||
expect(fetchCalls[0].url).toContain('/save?file=home')
|
||||
})
|
||||
|
||||
it('Cmd+P publishes', async () => {
|
||||
const ev = press(document, { key: 'p', metaKey: true })
|
||||
expect(ev.defaultPrevented).toBe(true)
|
||||
await flush() // publish awaits save() before its own fetch
|
||||
expect(fetchCalls.some(c => c.url === '/publish')).toBe(true)
|
||||
})
|
||||
|
||||
it('Ctrl+Shift+V opens the versions page in a new tab', () => {
|
||||
const ev = press(document, { key: 'V', ctrlKey: true, shiftKey: true })
|
||||
expect(ev.defaultPrevented).toBe(true)
|
||||
expect((window as any).open).toHaveBeenCalledWith('/versions?file=home', '_blank')
|
||||
})
|
||||
|
||||
it("'?' opens the shortcuts overlay and Esc closes it", () => {
|
||||
press(document, { key: '?' })
|
||||
expect(document.getElementById('shortcuts-overlay')).not.toBeNull()
|
||||
press(document, { key: 'Escape' })
|
||||
expect(document.getElementById('shortcuts-overlay')).toBeNull()
|
||||
})
|
||||
|
||||
it('plain typing in an input never triggers save', () => {
|
||||
const input = document.createElement('input')
|
||||
document.body.appendChild(input)
|
||||
const ev = press(input, { key: 's' })
|
||||
expect(ev.defaultPrevented).toBe(false)
|
||||
expect(fetchCalls.length).toBe(0)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user