Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
efabb5a
feat: extend Cypress.Keyboard.Keys and cy.press to support all keyboa…
jennifer-shehane Apr 11, 2025
88b8f76
Update cli/types/cypress.d.ts
jennifer-shehane Apr 11, 2025
c4af6b6
reference urls for codepoints
cacieprins Apr 25, 2025
70ecad0
type defs for expanded keys; tests
cacieprins Aug 6, 2025
4a84ff3
Merge branch 'develop' into feat-all-keys
cacieprins Aug 6, 2025
2485043
changelog
cacieprins Aug 6, 2025
935d766
rm modifier keys temporarily - see WIP @ feat/key-press-modifier-keys
cacieprins Aug 6, 2025
6df79a8
Add pr link to changelog
cacieprins Aug 6, 2025
bb031ba
reduce repetition with key codes; remove unsupported capslock keycode
cacieprins Aug 6, 2025
e9abde8
only "key" special function keys in Cypress.Keyboard.Keys - otherwise…
cacieprins Aug 6, 2025
bde74b4
do not refocus on f6 - f6 behavior keyup is indeterminate, so do not …
cacieprins Aug 7, 2025
eddde62
Merge branch 'develop' into feat-all-keys
cacieprins Aug 20, 2025
6109992
clean up keyboard key types, clean up duplicate def from merge
cacieprins Aug 20, 2025
385ecc9
various cleanup - wip - need multi-codepoint fix
cacieprins Aug 20, 2025
54f4f26
support multi-codepoint characters
cacieprins Aug 20, 2025
16a44f2
Merge branch 'develop' into feat-all-keys
cacieprins Aug 20, 2025
bad947c
properly dispatch each part of a multipart utf-8 character
cacieprins Aug 20, 2025
9656b4c
fix import
cacieprins Aug 21, 2025
f5fb925
fix dtslint
cacieprins Aug 21, 2025
8bf80af
tscheck
cacieprins Aug 21, 2025
32cc334
fix spacing
cacieprins Aug 21, 2025
df6ef92
Merge branch 'develop' into feat-all-keys
cacieprins Aug 21, 2025
aa9a386
Apply suggestions from code review
cacieprins Aug 21, 2025
88d776c
Merge branch 'develop' into feat-all-keys
cacieprins Aug 22, 2025
0944c46
changelog
cacieprins Aug 22, 2025
485e7d6
changelog
cacieprins Aug 22, 2025
af4c50c
ensure input actions are released in bidi; add test for keypress and …
cacieprins Aug 25, 2025
166e305
fix keypress & input events in chrome
cacieprins Aug 25, 2025
707adf0
consistent debug
cacieprins Aug 25, 2025
869912a
rm debug logging from input fixture
cacieprins Aug 25, 2025
f63b212
Merge branch 'develop' into feat-all-keys
cacieprins Aug 25, 2025
cb9d042
some typos, changelog version
cacieprins Aug 26, 2025
d43600b
fix toSupportedKey guard fn to properly reject non-strings
cacieprins Aug 26, 2025
d9085ff
add Space as named key, remove warnings re legacy firefox
cacieprins Aug 26, 2025
47ed81f
fix space
cacieprins Aug 26, 2025
311dc66
support single-digit number keys
cacieprins Aug 26, 2025
03a840e
remove support for F-keys
cacieprins Aug 27, 2025
3878191
add test cmd to types pkg
cacieprins Aug 27, 2025
58d7804
rm failing vitest project config
cacieprins Aug 27, 2025
e413eda
fix changelog
cacieprins Aug 27, 2025
764a438
clean up types a bit for single digit numbers
cacieprins Aug 27, 2025
fd8fd80
more updates
cacieprins Aug 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
## 15.0.1
## 15.1.0

_Released 08/26/2025 (PENDING)_

**Features:**

- 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).

**Bugfixes:**

- Fixed an issue where the open Studio button would incorrectly show for component tests. Addressed in [#32315](https://github.com/cypress-io/cypress/pull/32315).
Expand Down
18 changes: 18 additions & 0 deletions cli/types/cypress-automation.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
declare namespace Cypress {
type SupportedNamedKey = 'ArrowDown' |
'ArrowLeft' |
'ArrowRight' |
'ArrowUp' |
'End' |
'Home' |
'PageDown' |
'PageUp' |
'Space' |
'Enter' |
'Tab' |
'Backspace' |
'Delete' |
'Insert'

type SupportedKey = SupportedNamedKey | string | number
}
17 changes: 15 additions & 2 deletions cli/types/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// <reference path="./cypress-npm-api.d.ts" />
/// <reference path="./cypress-eventemitter.d.ts" />
/// <reference path="./cypress-type-helpers.d.ts" />

/// <reference path="./cypress-automation.d.ts" />
declare namespace Cypress {
type FileContents = string | any[] | object
type HistoryDirection = 'back' | 'forward'
Expand Down Expand Up @@ -684,7 +684,20 @@ declare namespace Cypress {
Keyboard: {
defaults(options: Partial<KeyboardDefaultsOptions>): void
Keys: {
DOWN: 'ArrowDown',
LEFT: 'ArrowLeft',
RIGHT: 'ArrowRight',
UP: 'ArrowUp',
END: 'End',
HOME: 'Home',
PAGEDOWN: 'PageDown',
PAGEUP: 'PageUp',
ENTER: 'Enter',
TAB: 'Tab',
BACKSPACE: 'Backspace',
SPACE: 'Space',
DELETE: 'Delete',
INSERT: 'Insert',
},
}

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

/**
* Get the immediately preceding sibling of each element in a set of the elements.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"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",
"stop-only-all": "yarn stop-only --folder packages",
"pretest": "yarn ensure-deps",
"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}",
"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}",
"test-debug": "lerna exec yarn test-debug --ignore=@packages/{driver,root,static,web-config}",
"test-integration": "lerna exec yarn test-integration --ignore=@packages/{driver,root,static,web-config}",
"test-mocha": "mocha --reporter spec scripts/spec.js",
Expand Down
56 changes: 42 additions & 14 deletions packages/driver/cypress/e2e/commands/actions/press.cy.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
describe('src/cy/commands/actions/press', () => {
it('dispatches the tab keypress to the AUT', () => {
// Non-BiDi firefox is not supported
if (Cypress.browser.family === 'firefox' && Cypress.browserMajorVersion() < 135) {
return
}

// TODO: Webkit is not supported. https://github.com/cypress-io/cypress/issues/31054
if (Cypress.isBrowser('webkit')) {
return
}
// TODO: Webkit is not supported. https://github.com/cypress-io/cypress/issues/31054
if (Cypress.isBrowser('webkit')) {
return
}

beforeEach(() => {
cy.visit('/fixtures/input_events.html')
})

cy.press(Cypress.Keyboard.Keys.TAB)

cy.get('#keydown').should('have.value', 'Tab')
it('fires the click event on the button when the named key is sent', () => {
cy.get('#button').focus()
cy.get('#button').should('be.focused')
cy.press(Cypress.Keyboard.Keys.SPACE)
cy.get('#checkbox').should('be.checked')
})

cy.get('#keyup').should('have.value', 'Tab')
it('fires the click event on the button when a space is sent', () => {
cy.get('#button').focus()
cy.get('#button').should('be.focused')
cy.press(' ')
cy.get('#checkbox').should('be.checked')
})

const testKeyDownUp = (key) => {
Copy link
Contributor

@adamalston adamalston Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should key be typed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It really doesn't matter here.

it(`dispatches ${key} keypress to the AUT`, () => {
cy.press(key)
// spacebar is a special case - it's both a named key and a single character,
// but when we dispatch the named key (via codepoint in bidi, via `Space` in CDP)
// we get the space character, not the name of the key.
cy.get('#keydown').should('have.value', key === 'Space' ? ' ' : key)
})
}

Object.values(Cypress.Keyboard.Keys).forEach(testKeyDownUp)

// sets truncated for speed

// // Numbers
;['0', '1'].forEach(testKeyDownUp)

;[0, 1].forEach(testKeyDownUp)

// // Letters
;['a', 'z'].forEach(testKeyDownUp)

// // Special characters
;['!', ' ', '€', 'é'].forEach(testKeyDownUp)
})
20 changes: 17 additions & 3 deletions packages/driver/cypress/fixtures/input_events.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,29 @@
<head>
<title>Input Event Monitor</title>
<script defer>
['keydown', 'keyup'].forEach((ev) => {
['keydown', 'keyup', 'keypress', 'input'].forEach((ev) => {
document.addEventListener(ev, (data) => {
document.getElementById(ev).value = data.key
console.log(ev, data)
document.getElementById(ev).value += data.key ?? data.data
})
})
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('button').addEventListener('click', (data) => {
console.log('button click', data)
document.getElementById('checkbox').checked = !document.getElementById('checkbox').checked
})
})
</script>
</head>

<body>
<input type="text" id="keyup" />
<input type="text" id="keydown" />
<input type="text" id="keyup" />

<input type="text" id="keypress" />
<input type="text" id="input_source" />
<input type="text" id="input" />

<input type="button" id="button" onClick=""/>
<input type="checkbox" id="checkbox" />
</body>
3 changes: 2 additions & 1 deletion packages/driver/cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
// We are setting sourceMap=true in order to have full codeFrame support within the driver package.
// We need to do this because we are using the standalone @cypress/webpack-preprocessor, which doesn't
// default the sourceMap option for us
"sourceMap": true
"sourceMap": true,
"moduleResolution": "node"
},
"include": ["**/*.ts"]
}
18 changes: 3 additions & 15 deletions packages/driver/src/cy/commands/actions/press.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import type { $Cy } from '../../../cypress/cy'
import type { StateFunc } from '../../../cypress/state'
import type { KeyPressSupportedKeys, AutomationCommands } from '@packages/types'
import { isSupportedKey, SupportedKey, AutomationCommands } from '@packages/types'
import { defaults } from 'lodash'
import { isSupportedKey } from '@packages/server/lib/automation/commands/key_press'
import $errUtils from '../../../cypress/error_utils'
import $utils from '../../../cypress/utils'

export interface PressCommand {
(key: KeyPressSupportedKeys, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>): void
(key: SupportedKey | string, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>): void
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the | string redundant given SupportedKey includes it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because SupportedKey always resolves to never unless the value is cast with toSupportedKey.

}

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

if (Cypress.browser.name === 'firefox' && Number(Cypress.browser.majorVersion) < 135) {
$errUtils.throwErrByPath('press.unsupported_browser_version', {
onFail: log,
args: {
browser: Cypress.browser.name,
version: Cypress.browser.majorVersion,
minimumVersion: 135,
},
})
}

try {
const command: 'key:press' = 'key:press'
const args: AutomationCommands[typeof command]['dataType'] = {
Expand Down
16 changes: 14 additions & 2 deletions packages/driver/src/cy/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import $utils from '../cypress/utils'
import $window from '../dom/window'
import type { Log } from '../cypress/log'
import type { StateFunc } from '../cypress/state'
import type { KeyPressSupportedKeys } from '@packages/types'

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

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

const Keys: Record<string, KeyPressSupportedKeys> = {
const Keys: Record<string, Cypress.SupportedNamedKey> = {
DOWN: 'ArrowDown',
LEFT: 'ArrowLeft',
RIGHT: 'ArrowRight',
UP: 'ArrowUp',
END: 'End',
HOME: 'Home',
PAGEDOWN: 'PageDown',
PAGEUP: 'PageUp',
ENTER: 'Enter',
TAB: 'Tab',
BACKSPACE: 'Backspace',
SPACE: 'Space',
DELETE: 'Delete',
INSERT: 'Insert',
}

export default {
Expand Down
25 changes: 17 additions & 8 deletions packages/driver/src/cypress/error_messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1311,15 +1311,24 @@ export default {
},

press: {
invalid_key: stripIndent`\
\`{{key}}\` is not supported by ${cmd('press')}. See \`Cypress.Keyboard.Keys\` for keys that are supported.
`,
unsupported_browser_version: stripIndent`\
${cmd('press')} is not supported in {{browser}} version {{version}}. Upgrade to version {{minimumVersion}} to use \`cy.press()\`.
`,
unsupported_browser: stripIndent`\
invalid_key: {
message: stripIndent`\
\`{{key}}\` is not supported by ${cmd('press')}.
`,
docsUrl: 'https://on.cypress.io/press',
},
unsupported_browser_version: {
message: stripIndent`\
${cmd('press')} is not supported in {{browser}} version {{version}}. Upgrade to version {{minimumVersion}} to use \`cy.press()\`.
`,
docsUrl: 'https://on.cypress.io/press',
},
unsupported_browser: {
message: stripIndent`\
${cmd('press')} is not supported in {{family}} browsers.
`,
`,
docsUrl: 'https://on.cypress.io/press',
},
},

proxy: {
Expand Down
39 changes: 10 additions & 29 deletions packages/driver/test/unit/cy/commands/actions/press.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @vitest-environment jsdom
*/
import { vi, describe, it, expect, beforeEach, Mock, MockedObject } from 'vitest'
import type { KeyPressSupportedKeys } from '@packages/types'

import addCommand, { PressCommand } from '../../../../../src/cy/commands/actions/press'
import type { $Cy } from '../../../../../src/cypress/cy'
import type { StateFunc } from '../../../../../src/cypress/state'
Expand All @@ -25,7 +25,6 @@ vi.mock('../../../../../src/cypress/error_utils', async () => {
})

describe('cy/commands/actions/press', () => {
let log: Mock<typeof Cypress['log']>
let automation: Mock<typeof Cypress['automation']>
let press: PressCommand
let Cypress: MockedObject<Cypress.Cypress>
Expand All @@ -36,13 +35,11 @@ describe('cy/commands/actions/press', () => {
let logReturnValue: Cypress.Log

beforeEach(() => {
log = vi.fn<typeof Cypress['log']>()
automation = vi.fn<typeof Cypress['automation']>()

Cypress = {
// The overloads for `log` don't get applied correctly here
// @ts-expect-error
log,
// @ts-expect-error - <Cypress['log']> has a type conflict between Driver and CLI d.ts
log: vi.fn(),
automation,
// @ts-expect-error
browser: {
Expand Down Expand Up @@ -95,14 +92,15 @@ describe('cy/commands/actions/press', () => {
})

describe('with a valid key', () => {
const key: KeyPressSupportedKeys = Keyboard.Keys.TAB

it('dispatches a key:press automation command', async () => {
await press(key)
expect(automation).toHaveBeenCalledWith('key:press', { key })
})
for (const key of Object.values(Keyboard.Keys)) {
it(`dispatches a key:press automation command for key: ${key}`, async () => {
await press(key)
expect(automation).toHaveBeenCalledWith('key:press', { key })
})
}

describe('with options', () => {
const key = 'Tab'
let options: Cypress.Loggable & Cypress.Timeoutable

beforeEach(() => {
Expand Down Expand Up @@ -152,23 +150,6 @@ describe('cy/commands/actions/press', () => {
})
})

describe('when in firefox below 135', () => {
it('throws an unsupported browser version error', async () => {
Cypress.browser.name = 'firefox'
Cypress.browser.majorVersion = '134'
await expect(press('Tab')).rejects.toThrow('`cy.press()` is not supported in firefox version 134. Upgrade to version 135 to use `cy.press()`.')

expect($errUtils.throwErrByPath).toHaveBeenCalledWith('press.unsupported_browser_version', {
onFail: logReturnValue,
args: {
browser: Cypress.browser.name,
version: Cypress.browser.majorVersion,
minimumVersion: 135,
},
})
})
})

describe('when automation throws', () => {
it('throws via $errUtils, passing in the results from Cypress.log', async () => {
const thrown = new Error('Some error')
Expand Down
Loading
Loading