Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
198 changes: 105 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 @@ -829,6 +737,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 +1744,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: 'Tab' /* todo */, options?: Partial<Loggable & 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.

should this be a direct reference to Keys.TAB? or the Key's interface?


/**
* Get the immediately preceding sibling of each element in a set of the elements.
*
Expand Down
22 changes: 5 additions & 17 deletions packages/driver/cypress/e2e/commands/actions/press.cy.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
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')
cy.get('#focus').focus()
cy.press('Tab')

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)
cy.get('#keyup').should('have.value', 'Tab')

return
}

throw e
}

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

cy.get('#keydown').should('have.value', 'Tab')
})
cy.get('#keydown').should('have.value', 'Tab')
})
})
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,
}
75 changes: 75 additions & 0 deletions packages/driver/src/cy/commands/actions/press.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { $Cy } from '../../../cypress/cy'
import type { StateFunc } from '../../../cypress/state'
import type { KeyPressSupportedKeys, AutomationCommands } from '@packages/types'
import { defaults } from 'lodash'
import { isSupportedKey } from '@packages/server/lib/automation/commands/key_press'
import $errUtils from '../../../cypress/error_utils'

export interface PressCommand {
(key: KeyPressSupportedKeys, userOptions?: Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable>): void
}

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>) {
const options: Cypress.Loggable & Partial<Cypress.Timeoutable> = defaults({}, userOptions, {
log: true,
})

const log = Cypress.log({
timeout: options.timeout,
hidden: options.log === false,
message: options,
consoleProps () {
return {
'Key': key,
}
},
})

if (!isSupportedKey(key)) {
$errUtils.throwErrByPath('press.invalid_key', {
onFail: log,
args: { key },
})

// throwErrByPath always throws, but there's no way to indicate that
// code beyond this point is unreachable to typescript / linters
return
}

if (Cypress.browser.family === 'webkit') {
$errUtils.throwErrByPath('press.unsupported_browser', {
onFail: log,
args: {
family: Cypress.browser.family,
},
})

return
}

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'] = {
key,
}

await Cypress.automation('key:press', args)
} catch (err) {
$errUtils.throwErr(err, { onFail: log })
}
}

return Commands.add('press', pressCommand)
}
8 changes: 4 additions & 4 deletions packages/driver/src/cypress/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { allCommands } from '../cy/commands'
import { addCommand as addNetstubbingCommand } from '../cy/net-stubbing'
import $errUtils from './error_utils'
import $stackUtils from './stack_utils'

import type { $Cy } from './cy'
import type { QueryFunction } from './state'

const PLACEHOLDER_COMMANDS = ['mount', 'hover']
Expand Down Expand Up @@ -40,7 +40,7 @@ const internalError = (path, args) => {
}

export default {
create: (Cypress, cy, state, config) => {
create: (Cypress: Cypress.Cypress, cy: $Cy, state, config) => {
const reservedCommandNames = new Set(Object.keys(cy))
const commands = {}
const queries = {}
Expand All @@ -63,11 +63,11 @@ export default {
return null
},

addSync (name, fn) {
addSync (name: string, fn: (...args: any[]) => any) {
return cy.addCommandSync(name, fn)
},

addAll (options = {}, obj) {
addAll (options, obj) {
if (!obj) {
obj = options
options = {}
Expand Down
12 changes: 12 additions & 0 deletions packages/driver/src/cypress/error_messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,18 @@ 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`\
${cmd('press')} is not supported in {{family}} browsers.
`,
},

proxy: {
js_rewriting_failed: stripIndent`\
An error occurred in the Cypress proxy layer while rewriting your source code. This is a bug in Cypress. Open an issue if you see this message.
Expand Down
Loading
Loading