-
Notifications
You must be signed in to change notification settings - Fork 62
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
fix(ct): correctly support custom element commands #915
Changes from all commits
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 |
---|---|---|
|
@@ -8,6 +8,7 @@ const wdioBrowserCommands = [ | |
"$", | ||
"action", | ||
"actions", | ||
"addCommand", | ||
"call", | ||
"custom$$", | ||
"custom$", | ||
|
@@ -23,6 +24,7 @@ const wdioBrowserCommands = [ | |
"mockClearAll", | ||
"mockRestoreAll", | ||
"newWindow", | ||
"overwriteCommand", | ||
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. these commands were skipped for some reason |
||
"pause", | ||
"react$$", | ||
"react$", | ||
|
@@ -54,8 +56,8 @@ const wdioElementCommands = [ | |
"dragAndDrop", | ||
"getAttribute", | ||
"getCSSProperty", | ||
"getComputedRole", | ||
"getComputedLabel", | ||
"getComputedRole", | ||
"getHTML", | ||
"getLocation", | ||
"getProperty", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,7 +88,7 @@ const overwriteBrowserCommands = (session, callstack) => | |
overwriteCommands({ | ||
session, | ||
callstack, | ||
commands: cmds.getBrowserCommands(), | ||
commands: cmds.getBrowserCommands().filter(cmd => !shouldNotWrapCommand(cmd)), | ||
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. Here I should skip |
||
elementScope: false, | ||
}); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,13 @@ export default class ProxyDriver { | |
commandWrapper: VoidFunction | undefined, | ||
): unknown { | ||
const monad = webdriverMonad(params, modifier, getWdioPrototype(userPrototype)); | ||
return monad(window.__testplane__.sessionId, commandWrapper); | ||
const browser = monad(window.__testplane__.sessionId, commandWrapper); | ||
|
||
window.__testplane__.customCommands.forEach(({ name, elementScope }) => { | ||
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. All custom commands are now added using the |
||
browser.addCommand(name, mockCommand(name), elementScope); | ||
}); | ||
|
||
return browser; | ||
} | ||
} | ||
|
||
|
@@ -67,25 +73,23 @@ function getAllProtocolCommands(): string[] { | |
} | ||
|
||
function getMockedProtocolCommands(): PropertiesObject { | ||
return [...getAllProtocolCommands(), ...SERVER_HANDLED_COMMANDS, ...window.__testplane__.customCommands].reduce( | ||
(acc, commandName) => { | ||
acc[commandName] = { value: mockCommand(commandName) }; | ||
return acc; | ||
}, | ||
{} as PropertiesObject, | ||
); | ||
return [...getAllProtocolCommands(), ...SERVER_HANDLED_COMMANDS].reduce((acc, commandName) => { | ||
acc[commandName] = { value: mockCommand(commandName) }; | ||
return acc; | ||
}, {} as PropertiesObject); | ||
} | ||
|
||
function mockCommand(commandName: string): ProtocolCommandFn { | ||
return async (...args: unknown[]): Promise<unknown> => { | ||
return async function (this: WebdriverIO.Browser | WebdriverIO.Element, ...args: unknown[]): Promise<unknown> { | ||
const { socket } = window.__testplane__; | ||
const timeout = getCommandTimeout(commandName); | ||
const element = isWdioElement(this) ? this : undefined; | ||
DudaGod marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
try { | ||
// TODO: remove type casting after https://github.com/socketio/socket.io/issues/4925 | ||
const [error, result] = (await socket | ||
.timeout(timeout) | ||
.emitWithAck(BrowserEventNames.runBrowserCommand, { name: commandName, args })) as [ | ||
.emitWithAck(BrowserEventNames.runBrowserCommand, { name: commandName, args, element })) as [ | ||
err: null | Error, | ||
result?: unknown, | ||
]; | ||
|
@@ -167,3 +171,7 @@ function truncate(value: string, maxLen: number): string { | |
|
||
return `${value.slice(0, maxLen - 3)}...`; | ||
} | ||
|
||
function isWdioElement(ctx: WebdriverIO.Browser | WebdriverIO.Element): ctx is WebdriverIO.Element { | ||
return Boolean((ctx as WebdriverIO.Element).elementId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export default function getLogger(): typeof console { | ||
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. I found that after using |
||
return { | ||
log: (): void => {}, | ||
info: (): void => {}, | ||
warn: (): void => {}, | ||
error: (): void => {}, | ||
} as unknown as typeof console; | ||
} | ||
|
||
getLogger.setLogLevelsConfig = (): void => {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default (): void => {}; | ||
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. Default mock for most libraries that only use |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const resolve = (): void => {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,21 +14,16 @@ import type { Plugin, Rollup } from "vite"; | |
const debug = createDebug("vite:plugin:generateIndexHtml"); | ||
|
||
// modules that used only in NodeJS environment and don't need to be compiled | ||
const MODULES_TO_MOCK = ["import-meta-resolve", "puppeteer-core", "archiver", "@wdio/repl"]; | ||
const DEFAULT_MODULES_TO_MOCK = ["puppeteer-core", "archiver", "@wdio/repl"]; | ||
const POLYFILLS = [...builtinModules, ...builtinModules.map(m => `node:${m}`)]; | ||
|
||
const virtualDriverModuleId = "virtual:@testplane/driver"; | ||
const virtualMockModuleId = "virtual:@testplane/mock"; | ||
|
||
const virtualModules = { | ||
driver: { | ||
id: virtualDriverModuleId, | ||
resolvedId: `\0${virtualDriverModuleId}`, | ||
}, | ||
mock: { | ||
id: virtualMockModuleId, | ||
resolvedId: `\0${virtualMockModuleId}`, | ||
}, | ||
}; | ||
|
||
export const plugin = async (): Promise<Plugin[]> => { | ||
|
@@ -46,6 +41,15 @@ export const plugin = async (): Promise<Plugin[]> => { | |
|
||
const automationProtocolPath = `/@fs${driverModulePath}`; | ||
|
||
const mockDefaultModulePath = path.resolve(browserModulesPath, "mock/default-module.js"); | ||
const mockImportMetaResolvePath = path.resolve(browserModulesPath, "mock/import-meta-resolve.js"); | ||
const mockWdioLoggerPath = path.resolve(browserModulesPath, "mock/@wdio-logger.js"); | ||
|
||
const modulesToMock = DEFAULT_MODULES_TO_MOCK.reduce((acc, val) => _.set(acc, val, mockDefaultModulePath), { | ||
"@wdio/logger": mockWdioLoggerPath, | ||
"import-meta-resolve": mockImportMetaResolvePath, | ||
}) as Record<string, string>; | ||
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. here, instead of using virtual modules, I started using mock files from the file system |
||
|
||
return [ | ||
{ | ||
name: "testplane:generateIndexHtml", | ||
|
@@ -114,19 +118,15 @@ export const plugin = async (): Promise<Plugin[]> => { | |
return polyfillPath(id.replace("/promises", "")); | ||
} | ||
|
||
if (MODULES_TO_MOCK.includes(id)) { | ||
return virtualModules.mock.resolvedId; | ||
if (Object.keys(modulesToMock).includes(id)) { | ||
return modulesToMock[id]; | ||
} | ||
}, | ||
|
||
load: (id: string): Rollup.LoadResult | void => { | ||
if (id === virtualModules.driver.resolvedId) { | ||
return `export const automationProtocolPath = ${JSON.stringify(automationProtocolPath)};`; | ||
} | ||
|
||
if (id === virtualModules.mock.resolvedId) { | ||
return ["export default () => {};", "export const resolve = () => ''"].join("\n"); | ||
} | ||
}, | ||
|
||
transform(code, id): Rollup.TransformResult { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,16 +104,23 @@ export class TestRunner extends NodejsEnvTestRunner { | |
const { publicAPI: session } = browser; | ||
|
||
return async (payload, cb): Promise<void> => { | ||
const { name, args } = payload; | ||
const cmdName = name as keyof typeof session; | ||
|
||
if (typeof session[cmdName] !== "function") { | ||
cb([prepareData<Error>(new Error(`"browser.${name}" does not exists in browser instance`))]); | ||
const { name, args, element } = payload; | ||
|
||
const wdioInstance = await getWdioInstance(session, element); | ||
const wdioInstanceName = element ? "element" : "browser"; | ||
const cmdName = name as keyof typeof wdioInstance; | ||
|
||
if (typeof wdioInstance[cmdName] !== "function") { | ||
cb([ | ||
prepareData<Error>( | ||
new Error(`"${wdioInstanceName}.${name}" does not exists in ${wdioInstanceName} instance`), | ||
), | ||
]); | ||
return; | ||
} | ||
|
||
try { | ||
const result = await (session[cmdName] as (...args: unknown[]) => Promise<unknown>)(...args); | ||
const result = await (wdioInstance[cmdName] as (...args: unknown[]) => Promise<unknown>)(...args); | ||
|
||
if (_.isError(result)) { | ||
return cb([prepareData<Error>(result)]); | ||
|
@@ -209,3 +216,20 @@ function transformExpectArg(arg: any): unknown { | |
|
||
return arg; | ||
} | ||
|
||
async function getWdioInstance( | ||
session: WebdriverIO.Browser, | ||
element?: WebdriverIO.Element, | ||
): Promise<WebdriverIO.Browser | WebdriverIO.Element> { | ||
const wdioInstance = element ? await session.$(element) : session; | ||
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. Here element is found by |
||
|
||
if (isWdioElement(wdioInstance) && !wdioInstance.selector) { | ||
wdioInstance.selector = element?.selector as Selector; | ||
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. I need recover selector because it is undefined when element found by |
||
} | ||
|
||
return wdioInstance; | ||
} | ||
|
||
function isWdioElement(ctx: WebdriverIO.Browser | WebdriverIO.Element): ctx is WebdriverIO.Element { | ||
return Boolean((ctx as WebdriverIO.Element).elementId); | ||
} |
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.
There was a mistake here. It is not possible to check the presence of a command on an element in this way. also, if the user has a custom command with the same name on the browser and the element (our case with
assertView
for example), then it was added only for the browser instance.So I rewrite it to use command list for browser and element from history.