-
Notifications
You must be signed in to change notification settings - Fork 3.3k
chore: create infrastructure to support backend function in cy.prompt #31803
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
Changes from 15 commits
698b608
308fa25
085754e
f1296d3
81de355
e7365c3
4020676
633c14c
d3d6def
4408bbf
ac5407b
629bd10
80e0146
b9de91d
c50e7d7
fccf329
342dd99
d1a1186
0df5fb4
f90abcd
ad3c467
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,17 @@ | ||
describe('src/cy/commands/prompt', () => { | ||
it('executes the prompt command', () => { | ||
cy.visit('/fixtures/dom.html') | ||
cy.visit('http://www.foobar.com:3500/fixtures/dom.html') | ||
|
||
// TODO: add more tests when cy.prompt is built out, but for now this just | ||
// verifies that the command executes without throwing an error | ||
// @ts-expect-error - this will not error when we actually release the experimentalPromptCommand flag | ||
cy.prompt('Hello, world!') | ||
|
||
cy.visit('http://www.barbaz.com:3500/fixtures/dom.html') | ||
|
||
cy.origin('http://www.barbaz.com:3500', () => { | ||
// @ts-expect-error - this will not error when we actually release the experimentalPromptCommand flag | ||
cy.prompt('Hello, world!') | ||
}) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,24 @@ | ||
export const handleSocketEvents = (Cypress) => { | ||
const onRequest = async (event, args) => { | ||
// The last argument is the callback, pop that off before messaging primary and call it with the response. | ||
const callback = args.pop() | ||
const response = await Cypress.specBridgeCommunicator.toPrimaryPromise({ | ||
event, | ||
data: { args }, | ||
timeout: Cypress.config().defaultCommandTimeout, | ||
}) | ||
const onRequest = async (event, args) => { | ||
// The last argument is the callback, pop that off before messaging primary and call it with the response. | ||
const callback = args.pop() | ||
const response = await Cypress.specBridgeCommunicator.toPrimaryPromise<{ error?: string, response?: any }>({ | ||
event, | ||
data: { args }, | ||
timeout: Cypress.config().defaultCommandTimeout, | ||
}) | ||
|
||
if (response && response.error) { | ||
return callback({ error: response.error }) | ||
} | ||
|
||
callback({ response }) | ||
if (response && response.error) { | ||
return callback({ error: response.error }) | ||
} | ||
|
||
Cypress.on('backend:request', (...args) => onRequest('backend:request', args)) | ||
Cypress.on('automation:request', (...args) => onRequest('automation:request', args)) | ||
callback({ response }) | ||
} | ||
|
||
export const handleCrossOriginSocketEvent = (Cypress, eventName: string) => { | ||
Cypress.on(eventName, (...args) => onRequest(eventName, args)) | ||
} | ||
|
||
export const handleDefaultCrossOriginSocketEvents = (Cypress) => { | ||
handleCrossOriginSocketEvent(Cypress, 'backend:request') | ||
handleCrossOriginSocketEvent(Cypress, 'automation:request') | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,55 @@ | ||
export interface CypressInternal extends Cypress.Cypress { | ||
backend: (eventName: string, ...args: any[]) => Promise<any> | ||
import type Emitter from 'component-emitter' | ||
|
||
interface InternalActions extends Cypress.Actions { | ||
( | ||
eventName: 'prompt:backend:request', | ||
listener: (...args: any[]) => void | ||
): Cypress.Cypress | ||
} | ||
|
||
export interface CypressInternalBase extends Cypress.Cypress { | ||
backendRequestHandler: ( | ||
backendRequestNamespace: string, | ||
eventName: string, | ||
...args: any[] | ||
) => Promise<any> | ||
on: InternalActions | ||
} | ||
|
||
interface CrossOriginCypressInternal extends CypressInternalBase { | ||
isCrossOriginSpecBridge: true | ||
handleCrossOriginSocketEvent: ( | ||
Cypress: CypressInternal, | ||
eventName: string | ||
) => void | ||
} | ||
|
||
interface SameOriginCypressInternal extends CypressInternalBase { | ||
isCrossOriginSpecBridge: false | ||
handlePrimaryOriginSocketEvent: ( | ||
Cypress: CypressInternal, | ||
eventName: string | ||
) => void | ||
} | ||
|
||
export type CypressInternal = | ||
| CrossOriginCypressInternal | ||
| SameOriginCypressInternal | ||
|
||
export interface CyPromptEventManager { | ||
ws: Emitter | ||
} | ||
|
||
export interface CyPromptOptions { | ||
Cypress: CypressInternal | ||
cy: Cypress.cy | ||
// Note that the eventManager is present in same origin AUTs, but not cross origin | ||
// so we need to check for it's presence before using it | ||
eventManager?: CyPromptEventManager | ||
} | ||
|
||
export interface CyPromptDriverDefaultShape { | ||
cyPrompt: (Cypress: CypressInternal, text: string) => Promise<void> | ||
createCyPrompt: ( | ||
options: CyPromptOptions | ||
) => (text: string, commandOptions?: object) => Promise<void> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,6 +88,30 @@ interface AutomationError extends Error { | |
// Are we running Cypress in Cypress? (Used for E2E Testing for Cypress in Cypress only) | ||
const isCypressInCypress = document.defaultView !== top | ||
|
||
const handlePrimaryOriginSocketEvent = (Cypress, eventName: string) => { | ||
Cypress.primaryOriginCommunicator.on( | ||
eventName, | ||
async ({ args }: { args: [string, any[]] }, { source, responseEvent }) => { | ||
let response | ||
|
||
try { | ||
response = await Cypress.backendRequestHandler( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I'm missing something, but is this supposed to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The functionality is right, the name is confusing (wrong) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
eventName, | ||
...args, | ||
) | ||
} catch (error) { | ||
response = { error } | ||
} | ||
|
||
Cypress.primaryOriginCommunicator.toSource( | ||
source, | ||
responseEvent, | ||
response, | ||
) | ||
}, | ||
) | ||
} | ||
|
||
class $Cypress { | ||
cy: any | ||
chai: any | ||
|
@@ -161,6 +185,8 @@ class $Cypress { | |
sinon = sinon | ||
lolex = fakeTimers | ||
|
||
handlePrimaryOriginSocketEvent = handlePrimaryOriginSocketEvent | ||
|
||
static $: any | ||
static utils: any | ||
|
||
|
@@ -764,7 +790,7 @@ class $Cypress { | |
} | ||
} | ||
|
||
backend (eventName, ...args) { | ||
backendRequestHandler (backendRequestNamespace: string, eventName, ...args) { | ||
return new Promise((resolve, reject) => { | ||
const fn = function (reply) { | ||
const e = reply.error | ||
|
@@ -787,10 +813,14 @@ class $Cypress { | |
return resolve(reply.response) | ||
} | ||
|
||
return this.emit('backend:request', eventName, ...args, fn) | ||
return Cypress.emit(backendRequestNamespace, eventName, ...args, fn) | ||
}) | ||
} | ||
|
||
backend (eventName, ...args) { | ||
return this.backendRequestHandler('backend:request', eventName, ...args) | ||
} | ||
|
||
automation (eventName, ...args) { | ||
// wrap action in promise | ||
return new Promise((resolve, reject) => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also took this opportunity to remove these types. We won't officially add them until we are ready to release the experiment