Skip to content

feat: cy.press() #31398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c3f4f85
wip - cy.press() command
cacieprins Mar 25, 2025
5abbb60
cy command for dispatching key press/down/up events
cacieprins Mar 26, 2025
6f5da33
unit tests and failure cases for cy.press()
cacieprins Mar 27, 2025
4748f31
Cypress.Keyboard.Keys definition; fix command log message
cacieprins Mar 27, 2025
f0f5dc0
add keys to the internal keyboard type
cacieprins Mar 28, 2025
98063fc
auto-focus in cdp
cacieprins Mar 31, 2025
7ec4c8b
ensure aut iframe is focused before dispatching key events in bidi br…
cacieprins Mar 31, 2025
c8b61e8
update tests for cdp focus
cacieprins Mar 31, 2025
6600009
fixed tests for bidi
cacieprins Mar 31, 2025
30bfc52
lint
cacieprins Mar 31, 2025
a12e15f
Merge branch 'develop' into cacie/feat/cy-press-command
cacieprins Mar 31, 2025
2e84355
fix type ref in .d.ts
cacieprins Mar 31, 2025
a33bcef
linting
cacieprins Mar 31, 2025
a1f9a27
skip press() driver test in ff below v135
cacieprins Mar 31, 2025
1ca097c
Merge branch 'develop' into cacie/feat/cy-press-command
jennifer-shehane Apr 1, 2025
3637665
try all contexts for frame before failing due to missing/invalid cont…
cacieprins Apr 1, 2025
7744213
ensure error is error before accessing props
cacieprins Apr 1, 2025
2636de5
Merge branch 'develop' into cacie/feat/cy-press-command
cacieprins Apr 2, 2025
fe3a435
skip press driver test in webkit
cacieprins Apr 2, 2025
8956584
changelog
cacieprins Apr 2, 2025
39ffe62
debug automation middleware invocation for firefox flake
cacieprins Apr 2, 2025
9d60c4d
debug
cacieprins Apr 2, 2025
1b183e5
cache update
cacieprins Apr 3, 2025
dc42d30
use bidi automation middleware from connectToNewSpec rather than cons…
cacieprins Apr 3, 2025
14d8822
more comprehensive logging
cacieprins Apr 3, 2025
a13c69d
debug socket base, additional debug in automation
cacieprins Apr 4, 2025
da743c3
install firefox automation middleware on setup as well as connectToNe…
cacieprins Apr 4, 2025
98b12c7
Merge branch 'develop' into cacie/feat/cy-press-command
cacieprins Apr 4, 2025
9af2e37
unit tests for firefox-utils
cacieprins Apr 7, 2025
a9ac681
proper calledWith
cacieprins Apr 7, 2025
e01d3df
Merge branch 'develop' into cacie/feat/cy-press-command
cacieprins Apr 7, 2025
c30265b
Merge branch 'develop' into cacie/feat/cy-press-command
jennifer-shehane Apr 8, 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
2 changes: 1 addition & 1 deletion .circleci/cache-version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Bump this version to force CI to re-create the cache from scratch.

3-18-2024:4:25
4-3-2025
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. -->
## 14.2.2
## 14.3.0

_Released 4/8/2025 (PENDING)_

**Features:**

- The [`cy.press()`](https://on.cypress.io/api/press) command is now available. It supports dispatching native Tab keyboard events to the browser. Addresses [#31050](https://github.com/cypress-io/cypress/issues/31050). Addresses [#299](https://github.com/cypress-io/cypress/issues/299). Addressed in [#31398](https://github.com/cypress-io/cypress/pull/31398).

**Bugfixes:**

- Allows for `babel-loader` version 10 to be a peer dependency of `@cypress/webpack-preprocessor`. Fixed in [#31218](https://github.com/cypress-io/cypress/pull/31218).
Expand Down
201 changes: 108 additions & 93 deletions cli/types/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,99 +578,7 @@ declare namespace Cypress {
*/
stop(): void

Commands: {
/**
* Add a custom command
* @see https://on.cypress.io/api/commands
*/
add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void

/**
* Add a custom parent command
* @see https://on.cypress.io/api/commands#Parent-Commands
*/
add<T extends keyof Chainable>(name: T, options: CommandOptions & { prevSubject: false }, fn: CommandFn<T>): void

/**
* Add a custom child command
* @see https://on.cypress.io/api/commands#Child-Commands
*/
add<T extends keyof Chainable, S = any>(name: T, options: CommandOptions & { prevSubject: true }, fn: CommandFnWithSubject<T, S>): void

/**
* Add a custom child or dual command
* @see https://on.cypress.io/api/commands#Validations
*/
add<T extends keyof Chainable, S extends PrevSubject>(
name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
): void

/**
* Add a custom command that allows multiple types as the prevSubject
* @see https://on.cypress.io/api/commands#Validations#Allow-Multiple-Types
*/
add<T extends keyof Chainable, S extends PrevSubject>(
name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
): void

/**
* Add one or more custom commands
* @see https://on.cypress.io/api/commands
*/
addAll<T extends keyof Chainable>(fns: CommandFns): void

/**
* Add one or more custom parent commands
* @see https://on.cypress.io/api/commands#Parent-Commands
*/
addAll<T extends keyof Chainable>(options: CommandOptions & { prevSubject: false }, fns: CommandFns): void

/**
* Add one or more custom child commands
* @see https://on.cypress.io/api/commands#Child-Commands
*/
addAll<T extends keyof Chainable, S = any>(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject<S>): void

/**
* Add one or more custom commands that validate their prevSubject
* @see https://on.cypress.io/api/commands#Validations
*/
addAll<T extends keyof Chainable, S extends PrevSubject>(
options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
): void

/**
* Add one or more custom commands that allow multiple types as their prevSubject
* @see https://on.cypress.io/api/commands#Allow-Multiple-Types
*/
addAll<T extends keyof Chainable, S extends PrevSubject>(
options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject<PrevSubjectMap<void>[S]>,
): void

/**
* Overwrite an existing Cypress command with a new implementation
* @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands
*/
overwrite<T extends keyof Chainable>(name: T, fn: CommandFnWithOriginalFn<T>): void

/**
* Overwrite an existing Cypress command with a new implementation
* @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands
*/
overwrite<T extends keyof Chainable, S extends PrevSubject>(name: T, fn: CommandFnWithOriginalFnAndSubject<T, PrevSubjectMap[S]>): void

/**
* Add a custom query
* @see https://on.cypress.io/api/custom-queries
*/
addQuery<T extends keyof Chainable>(name: T, fn: QueryFn<T>): void

/**
* Overwrite an existing Cypress query with a new implementation
* @see https://on.cypress.io/api/custom-queries
*/
overwriteQuery<T extends keyof Chainable>(name: T, fn: QueryFnWithOriginalFn<T>): void
}
Commands: Commands

/**
* @see https://on.cypress.io/cookies
Expand Down Expand Up @@ -775,6 +683,9 @@ declare namespace Cypress {
*/
Keyboard: {
defaults(options: Partial<KeyboardDefaultsOptions>): void
Keys: {
TAB: 'Tab',
},
}

/**
Expand Down Expand Up @@ -829,6 +740,100 @@ declare namespace Cypress {
onSpecWindow: (window: Window, specList: string[] | Array<() => Promise<void>>) => void
}

interface Commands {
/**
* Add a custom command
* @see https://on.cypress.io/api/commands
*/
add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void

/**
* Add a custom parent command
* @see https://on.cypress.io/api/commands#Parent-Commands
*/
add<T extends keyof Chainable>(name: T, options: CommandOptions & { prevSubject: false }, fn: CommandFn<T>): void

/**
* Add a custom child command
* @see https://on.cypress.io/api/commands#Child-Commands
*/
add<T extends keyof Chainable, S = any>(name: T, options: CommandOptions & { prevSubject: true }, fn: CommandFnWithSubject<T, S>): void

/**
* Add a custom child or dual command
* @see https://on.cypress.io/api/commands#Validations
*/
add<T extends keyof Chainable, S extends PrevSubject>(
name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
): void

/**
* Add a custom command that allows multiple types as the prevSubject
* @see https://on.cypress.io/api/commands#Validations#Allow-Multiple-Types
*/
add<T extends keyof Chainable, S extends PrevSubject>(
name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
): void

/**
* Add one or more custom commands
* @see https://on.cypress.io/api/commands
*/
addAll<T extends keyof Chainable>(fns: CommandFns): void

/**
* Add one or more custom parent commands
* @see https://on.cypress.io/api/commands#Parent-Commands
*/
addAll<T extends keyof Chainable>(options: CommandOptions & { prevSubject: false }, fns: CommandFns): void

/**
* Add one or more custom child commands
* @see https://on.cypress.io/api/commands#Child-Commands
*/
addAll<T extends keyof Chainable, S = any>(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject<S>): void

/**
* Add one or more custom commands that validate their prevSubject
* @see https://on.cypress.io/api/commands#Validations
*/
addAll<T extends keyof Chainable, S extends PrevSubject>(
options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
): void

/**
* Add one or more custom commands that allow multiple types as their prevSubject
* @see https://on.cypress.io/api/commands#Allow-Multiple-Types
*/
addAll<T extends keyof Chainable, S extends PrevSubject>(
options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject<PrevSubjectMap<void>[S]>,
): void

/**
* Overwrite an existing Cypress command with a new implementation
* @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands
*/
overwrite<T extends keyof Chainable>(name: T, fn: CommandFnWithOriginalFn<T>): void

/**
* Overwrite an existing Cypress command with a new implementation
* @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands
*/
overwrite<T extends keyof Chainable, S extends PrevSubject>(name: T, fn: CommandFnWithOriginalFnAndSubject<T, PrevSubjectMap[S]>): void

/**
* Add a custom query
* @see https://on.cypress.io/api/custom-queries
*/
addQuery<T extends keyof Chainable>(name: T, fn: QueryFn<T>): void

/**
* Overwrite an existing Cypress query with a new implementation
* @see https://on.cypress.io/api/custom-queries
*/
overwriteQuery<T extends keyof Chainable>(name: T, fn: QueryFnWithOriginalFn<T>): void
}

type CanReturnChainable = void | Chainable | Promise<unknown>
type ThenReturn<S, R> =
R extends void ? Chainable<S> :
Expand Down Expand Up @@ -1742,6 +1747,16 @@ declare namespace Cypress {
*/
pause(options?: Partial<Loggable>): Chainable<Subject>

/**
* Send a native sequence of keyboard events: keydown & press, followed by keyup, for the provided key.
* Supported keys index the Cypress.Keyboard.Keys record.
*
* @example
* 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>): void

/**
* Get the immediately preceding sibling of each element in a set of the elements.
*
Expand Down
4 changes: 3 additions & 1 deletion cli/types/tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
"jsdoc-format": false,
// for now keep the Cypress NPM module API
// in its own file for simplicity
"no-single-declare-module": false
"no-single-declare-module": false,
// This is detecting necessary qualifiers as unnecessary
"no-unnecessary-qualifier": false
Copy link
Contributor

Choose a reason for hiding this comment

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

whats the reason we are doing this? is it related to the keys only having one value in it, i.e "tab"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was getting thrown by L#1758 - tslint thinks the Cypress reference here is the namespace, but it's the interface Cypress within the namespace.

(no-unnecessary-qualifier throws when you reference the namespace from within the namespace - generally you don't need to and shouldn't, because you're in scope with the namespace. But we have an interface named the same as the namespace, and tslint gets confused because of it)

},
"linterOptions": {
"exclude": [
Expand Down
28 changes: 13 additions & 15 deletions packages/driver/cypress/e2e/commands/actions/press.cy.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
describe('__placeholder__/commands/actions/press', () => {
describe('src/cy/commands/actions/press', () => {
it('dispatches the tab keypress to the AUT', () => {
cy.visit('/fixtures/input_events.html')
// Non-BiDi firefox is not supported
if (Cypress.browser.family === 'firefox' && Cypress.browserMajorVersion() < 135) {
return
}

cy.get('#focus').focus().then(async () => {
try {
await Cypress.automation('key:press', { key: 'Tab' })
} catch (e) {
if (e.message && (e.message as string).includes('key:press')) {
cy.log(e.message)
// TODO: Webkit is not supported. https://github.com/cypress-io/cypress/issues/31054
if (Cypress.isBrowser('webkit')) {
return
}

return
}
cy.visit('/fixtures/input_events.html')

throw e
}
cy.press(Cypress.Keyboard.Keys.TAB)

cy.get('#keyup').should('have.value', 'Tab')
cy.get('#keydown').should('have.value', 'Tab')

cy.get('#keydown').should('have.value', 'Tab')
})
cy.get('#keyup').should('have.value', 'Tab')
})
})
2 changes: 1 addition & 1 deletion packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ it('verifies number of cy commands', () => {
'invoke', 'its', 'getCookie', 'getCookies', 'setCookie', 'clearCookie', 'clearCookies', 'pause', 'debug', 'exec', 'readFile',
'writeFile', 'fixture', 'clearLocalStorage', 'url', 'hash', 'location', 'end', 'noop', 'log', 'wrap', 'reload', 'go', 'visit',
'focused', 'get', 'contains', 'shadow', 'within', 'request', 'session', 'screenshot', 'task', 'find', 'filter', 'not',
'children', 'eq', 'closest', 'first', 'last', 'next', 'nextAll', 'nextUntil', 'parent', 'parents', 'parentsUntil', 'prev',
'children', 'eq', 'closest', 'first', 'last', 'next', 'nextAll', 'nextUntil', 'parent', 'parents', 'parentsUntil', 'prev', 'press',
'prevAll', 'prevUntil', 'siblings', 'wait', 'title', 'window', 'document', 'viewport', 'server', 'route', 'intercept', 'origin',
'mount', 'as', 'root', 'getAllLocalStorage', 'clearAllLocalStorage', 'getAllSessionStorage', 'clearAllSessionStorage',
'getAllCookies', 'clearAllCookies',
Expand Down
1 change: 0 additions & 1 deletion packages/driver/cypress/fixtures/input_events.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
</head>

<body>
<input type="text" id="focus" />
<input type="text" id="keyup" />
<input type="text" id="keydown" />
</body>
2 changes: 2 additions & 0 deletions packages/driver/src/cy/commands/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Submit from './submit'
import * as Type from './type'
import * as Trigger from './trigger'
import * as Mount from './mount'
import Press from './press'

export {
Check,
Expand All @@ -22,4 +23,5 @@ export {
Type,
Trigger,
Mount,
Press,
}
Loading
Loading