Skip to content

Commit 90310ae

Browse files
feat: extend Cypress.Keyboard.Keys and cy.press to support (almost) all keyboard keys (#31496)
* feat: extend Cypress.Keyboard.Keys and cy.press to support all keyboard keys * Update cli/types/cypress.d.ts * reference urls for codepoints * type defs for expanded keys; tests * changelog * rm modifier keys temporarily - see WIP @ feat/key-press-modifier-keys * Add pr link to changelog * reduce repetition with key codes; remove unsupported capslock keycode * only "key" special function keys in Cypress.Keyboard.Keys - otherwise, support any single character even if multiple codepoints * do not refocus on f6 - f6 behavior keyup is indeterminate, so do not assert in that special case * clean up keyboard key types, clean up duplicate def from merge * various cleanup - wip - need multi-codepoint fix * support multi-codepoint characters * properly dispatch each part of a multipart utf-8 character * fix import * fix dtslint * tscheck * fix spacing * Apply suggestions from code review * changelog * changelog * ensure input actions are released in bidi; add test for keypress and input events for firefox * fix keypress & input events in chrome * consistent debug * rm debug logging from input fixture * some typos, changelog version * fix toSupportedKey guard fn to properly reject non-strings * add Space as named key, remove warnings re legacy firefox * fix space * support single-digit number keys * remove support for F-keys * add test cmd to types pkg * rm failing vitest project config * fix changelog * clean up types a bit for single digit numbers * more updates --------- Co-authored-by: Cacie Prins <[email protected]> Co-authored-by: Cacie Prins <[email protected]>
1 parent 3bb158d commit 90310ae

File tree

21 files changed

+681
-194
lines changed

21 files changed

+681
-194
lines changed

cli/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
2-
## 15.0.1
2+
## 15.1.0
33

44
_Released 08/26/2025 (PENDING)_
55

6+
**Features:**
7+
8+
- Expanded `cy.press()` to support more key types. Addresses [#31051](https://github.com/cypress-io/cypress/issues/31051) and [#31488](https://github.com/cypress-io/cypress/issues/31488). Addressed in [#31496](https://github.com/cypress-io/cypress/pull/31496).
9+
610
**Bugfixes:**
711

812
- Fixed an issue where OS distributions and releases were sometimes not properly populated for Module API results and Cloud recordings. Fixes [#30533](https://github.com/cypress-io/cypress/issues/30533). Addressed in [#32283](https://github.com/cypress-io/cypress/pull/32283).

cli/types/cypress-automation.d.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
declare namespace Cypress {
2+
type SupportedNamedKey = 'ArrowDown' |
3+
'ArrowLeft' |
4+
'ArrowRight' |
5+
'ArrowUp' |
6+
'End' |
7+
'Home' |
8+
'PageDown' |
9+
'PageUp' |
10+
'Space' |
11+
'Enter' |
12+
'Tab' |
13+
'Backspace' |
14+
'Delete' |
15+
'Insert'
16+
17+
type SupportedKey = SupportedNamedKey | string | number
18+
}

cli/types/cypress.d.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/// <reference path="./cypress-npm-api.d.ts" />
33
/// <reference path="./cypress-eventemitter.d.ts" />
44
/// <reference path="./cypress-type-helpers.d.ts" />
5-
5+
/// <reference path="./cypress-automation.d.ts" />
66
declare namespace Cypress {
77
type FileContents = string | any[] | object
88
type HistoryDirection = 'back' | 'forward'
@@ -684,7 +684,20 @@ declare namespace Cypress {
684684
Keyboard: {
685685
defaults(options: Partial<KeyboardDefaultsOptions>): void
686686
Keys: {
687+
DOWN: 'ArrowDown',
688+
LEFT: 'ArrowLeft',
689+
RIGHT: 'ArrowRight',
690+
UP: 'ArrowUp',
691+
END: 'End',
692+
HOME: 'Home',
693+
PAGEDOWN: 'PageDown',
694+
PAGEUP: 'PageUp',
695+
ENTER: 'Enter',
687696
TAB: 'Tab',
697+
BACKSPACE: 'Backspace',
698+
SPACE: 'Space',
699+
DELETE: 'Delete',
700+
INSERT: 'Insert',
688701
},
689702
}
690703

@@ -1765,7 +1778,7 @@ declare namespace Cypress {
17651778
* cy.press(Cypress.Keyboard.Keys.TAB) // dispatches a keydown and press event to the browser, followed by a keyup event.
17661779
* @see https://on.cypress.io/press
17671780
*/
1768-
press(key: typeof Cypress.Keyboard.Keys[keyof typeof Cypress.Keyboard.Keys], options?: Partial<Loggable & Timeoutable>): Chainable<null>
1781+
press(key: SupportedKey, options?: Partial<Loggable & Timeoutable>): Chainable<null>
17691782

17701783
/**
17711784
* Get the immediately preceding sibling of each element in a set of the elements.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"stop-only": "npx stop-only --skip .cy,.publish,.projects,node_modules,dist,dist-test,fixtures,lib,bower_components,src,__snapshots__,patches --exclude cypress-tests.ts,*only.cy.js",
5858
"stop-only-all": "yarn stop-only --folder packages",
5959
"pretest": "yarn ensure-deps",
60-
"test": "yarn lerna exec yarn test --scope=cypress --scope=@packages/{config,data-context,driver,electron,errors,extension,https-proxy,launcher,net-stubbing,network,packherd-require,proxy,rewriter,scaffold-config,socket,v8-snapshot-require,telemetry,stderr-filtering} --scope=@tooling/{electron-mksnapshot,v8-snapshot}",
60+
"test": "yarn lerna exec yarn test --scope=cypress --scope=@packages/{config,data-context,driver,electron,errors,extension,https-proxy,launcher,net-stubbing,network,packherd-require,proxy,rewriter,scaffold-config,socket,v8-snapshot-require,telemetry,stderr-filtering,types} --scope=@tooling/{electron-mksnapshot,v8-snapshot}",
6161
"test-debug": "lerna exec yarn test-debug --ignore=@packages/{driver,root,static,web-config}",
6262
"test-integration": "lerna exec yarn test-integration --ignore=@packages/{driver,root,static,web-config}",
6363
"test-mocha": "mocha --reporter spec scripts/spec.js",
Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,49 @@
11
describe('src/cy/commands/actions/press', () => {
2-
it('dispatches the tab keypress to the AUT', () => {
3-
// Non-BiDi firefox is not supported
4-
if (Cypress.browser.family === 'firefox' && Cypress.browserMajorVersion() < 135) {
5-
return
6-
}
7-
8-
// TODO: Webkit is not supported. https://github.com/cypress-io/cypress/issues/31054
9-
if (Cypress.isBrowser('webkit')) {
10-
return
11-
}
2+
// TODO: Webkit is not supported. https://github.com/cypress-io/cypress/issues/31054
3+
if (Cypress.isBrowser('webkit')) {
4+
return
5+
}
126

7+
beforeEach(() => {
138
cy.visit('/fixtures/input_events.html')
9+
})
1410

15-
cy.press(Cypress.Keyboard.Keys.TAB)
16-
17-
cy.get('#keydown').should('have.value', 'Tab')
11+
it('fires the click event on the button when the named key is sent', () => {
12+
cy.get('#button').focus()
13+
cy.get('#button').should('be.focused')
14+
cy.press(Cypress.Keyboard.Keys.SPACE)
15+
cy.get('#checkbox').should('be.checked')
16+
})
1817

19-
cy.get('#keyup').should('have.value', 'Tab')
18+
it('fires the click event on the button when a space is sent', () => {
19+
cy.get('#button').focus()
20+
cy.get('#button').should('be.focused')
21+
cy.press(' ')
22+
cy.get('#checkbox').should('be.checked')
2023
})
24+
25+
const testKeyDownUp = (key) => {
26+
it(`dispatches ${key} keypress to the AUT`, () => {
27+
cy.press(key)
28+
// spacebar is a special case - it's both a named key and a single character,
29+
// but when we dispatch the named key (via codepoint in bidi, via `Space` in CDP)
30+
// we get the space character, not the name of the key.
31+
cy.get('#keydown').should('have.value', key === 'Space' ? ' ' : key)
32+
})
33+
}
34+
35+
Object.values(Cypress.Keyboard.Keys).forEach(testKeyDownUp)
36+
37+
// sets truncated for speed
38+
39+
// // Numbers
40+
;['0', '1'].forEach(testKeyDownUp)
41+
42+
;[0, 1].forEach(testKeyDownUp)
43+
44+
// // Letters
45+
;['a', 'z'].forEach(testKeyDownUp)
46+
47+
// // Special characters
48+
;['!', ' ', '€', 'é'].forEach(testKeyDownUp)
2149
})

packages/driver/cypress/fixtures/input_events.html

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,29 @@
33
<head>
44
<title>Input Event Monitor</title>
55
<script defer>
6-
['keydown', 'keyup'].forEach((ev) => {
6+
['keydown', 'keyup', 'keypress', 'input'].forEach((ev) => {
77
document.addEventListener(ev, (data) => {
8-
document.getElementById(ev).value = data.key
8+
console.log(ev, data)
9+
document.getElementById(ev).value += data.key ?? data.data
10+
})
11+
})
12+
document.addEventListener('DOMContentLoaded', () => {
13+
document.getElementById('button').addEventListener('click', (data) => {
14+
console.log('button click', data)
15+
document.getElementById('checkbox').checked = !document.getElementById('checkbox').checked
916
})
1017
})
1118
</script>
1219
</head>
1320

1421
<body>
15-
<input type="text" id="keyup" />
1622
<input type="text" id="keydown" />
23+
<input type="text" id="keyup" />
24+
25+
<input type="text" id="keypress" />
26+
<input type="text" id="input_source" />
27+
<input type="text" id="input" />
28+
29+
<input type="button" id="button" onClick=""/>
30+
<input type="checkbox" id="checkbox" />
1731
</body>

packages/driver/cypress/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
// We are setting sourceMap=true in order to have full codeFrame support within the driver package.
1010
// We need to do this because we are using the standalone @cypress/webpack-preprocessor, which doesn't
1111
// default the sourceMap option for us
12-
"sourceMap": true
12+
"sourceMap": true,
13+
"moduleResolution": "node"
1314
},
1415
"include": ["**/*.ts"]
1516
}

packages/driver/src/cy/commands/actions/press.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import type { $Cy } from '../../../cypress/cy'
22
import type { StateFunc } from '../../../cypress/state'
3-
import type { KeyPressSupportedKeys, AutomationCommands } from '@packages/types'
3+
import { isSupportedKey, SupportedKey, AutomationCommands } from '@packages/types'
44
import { defaults } from 'lodash'
5-
import { isSupportedKey } from '@packages/server/lib/automation/commands/key_press'
65
import $errUtils from '../../../cypress/error_utils'
76
import $utils from '../../../cypress/utils'
87

98
export interface PressCommand {
10-
(key: KeyPressSupportedKeys, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>): void
9+
(key: SupportedKey | string, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>): void
1110
}
1211

1312
export default function (Commands: Cypress.Commands, Cypress: Cypress.Cypress, cy: $Cy, state: StateFunc, config: any) {
14-
async function pressCommand (key: KeyPressSupportedKeys, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>) {
13+
async function pressCommand (key: SupportedKey | string | number, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>) {
1514
const options: Cypress.Loggable & Partial<Cypress.Timeoutable> = defaults({}, userOptions, {
1615
log: true,
1716
})
@@ -50,17 +49,6 @@ export default function (Commands: Cypress.Commands, Cypress: Cypress.Cypress, c
5049
return null
5150
}
5251

53-
if (Cypress.browser.name === 'firefox' && Number(Cypress.browser.majorVersion) < 135) {
54-
$errUtils.throwErrByPath('press.unsupported_browser_version', {
55-
onFail: log,
56-
args: {
57-
browser: Cypress.browser.name,
58-
version: Cypress.browser.majorVersion,
59-
minimumVersion: 135,
60-
},
61-
})
62-
}
63-
6452
try {
6553
const command: 'key:press' = 'key:press'
6654
const args: AutomationCommands[typeof command]['dataType'] = {

packages/driver/src/cy/keyboard.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import $utils from '../cypress/utils'
1414
import $window from '../dom/window'
1515
import type { Log } from '../cypress/log'
1616
import type { StateFunc } from '../cypress/state'
17-
import type { KeyPressSupportedKeys } from '@packages/types'
1817

1918
const debug = Debug('cypress:driver:keyboard')
2019

@@ -1398,8 +1397,21 @@ const defaults = (props: Partial<Cypress.KeyboardDefaultsOptions>) => {
13981397
return getConfig()
13991398
}
14001399

1401-
const Keys: Record<string, KeyPressSupportedKeys> = {
1400+
const Keys: Record<string, Cypress.SupportedNamedKey> = {
1401+
DOWN: 'ArrowDown',
1402+
LEFT: 'ArrowLeft',
1403+
RIGHT: 'ArrowRight',
1404+
UP: 'ArrowUp',
1405+
END: 'End',
1406+
HOME: 'Home',
1407+
PAGEDOWN: 'PageDown',
1408+
PAGEUP: 'PageUp',
1409+
ENTER: 'Enter',
14021410
TAB: 'Tab',
1411+
BACKSPACE: 'Backspace',
1412+
SPACE: 'Space',
1413+
DELETE: 'Delete',
1414+
INSERT: 'Insert',
14031415
}
14041416

14051417
export default {

packages/driver/src/cypress/error_messages.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,15 +1311,24 @@ export default {
13111311
},
13121312

13131313
press: {
1314-
invalid_key: stripIndent`\
1315-
\`{{key}}\` is not supported by ${cmd('press')}. See \`Cypress.Keyboard.Keys\` for keys that are supported.
1316-
`,
1317-
unsupported_browser_version: stripIndent`\
1318-
${cmd('press')} is not supported in {{browser}} version {{version}}. Upgrade to version {{minimumVersion}} to use \`cy.press()\`.
1319-
`,
1320-
unsupported_browser: stripIndent`\
1314+
invalid_key: {
1315+
message: stripIndent`\
1316+
\`{{key}}\` is not supported by ${cmd('press')}.
1317+
`,
1318+
docsUrl: 'https://on.cypress.io/press',
1319+
},
1320+
unsupported_browser_version: {
1321+
message: stripIndent`\
1322+
${cmd('press')} is not supported in {{browser}} version {{version}}. Upgrade to version {{minimumVersion}} to use \`cy.press()\`.
1323+
`,
1324+
docsUrl: 'https://on.cypress.io/press',
1325+
},
1326+
unsupported_browser: {
1327+
message: stripIndent`\
13211328
${cmd('press')} is not supported in {{family}} browsers.
1322-
`,
1329+
`,
1330+
docsUrl: 'https://on.cypress.io/press',
1331+
},
13231332
},
13241333

13251334
proxy: {

0 commit comments

Comments
 (0)