Skip to content
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: performance #658

Draft
wants to merge 12 commits into
base: v9-support
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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 client-side-js/_checkForUI5Ready.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
async function clientSide__checkForUI5Ready(browserInstance) {
return await browserInstance.execute(async () => {
try {
await window.wdi5.waitForUI5(window.wdi5.waitForUI5Options)
await window.bridge.waitForUI5(window.wdi5.waitForUI5Options)
} catch (error) {
return window.wdi5.errorHandling(error)
}
Expand Down
4 changes: 2 additions & 2 deletions client-side-js/allControls.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ async function clientSide_allControls(controlSelector, browserInstance) {
}

try {
await window.wdi5.waitForUI5(waitForUI5Options)
await window.bridge.waitForUI5(waitForUI5Options)
} catch (error) {
return window.wdi5.errorHandling(error)
}

window.wdi5.Log.info("[browser wdi5] locating " + JSON.stringify(controlSelector))
controlSelector.selector = window.wdi5.createMatcher(controlSelector.selector)
let domElements;
let domElements

try {
domElements = await window.bridge.findAllDOMElementsByControlSelector(controlSelector)
Expand Down
201 changes: 99 additions & 102 deletions client-side-js/executeControlMethod.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,118 +13,115 @@ if (global.browser) {
async function executeControlMethod(webElement, methodName, browserInstance, args) {
return await browserInstance.execute(
async (webElement, methodName, args) => {
return new Promise(async (resolve, reject) => {
await window.wdi5.waitForUI5(
window.wdi5.waitForUI5Options,
() => {
// DOM to UI5
const oControl = window.wdi5.getUI5CtlForWebObj(webElement)
try {
await window.bridge.waitForUI5(window.wdi5.waitForUI5Options)

// execute the function
let result = oControl[methodName].apply(oControl, args)
const metadata = oControl.getMetadata()
// DOM to UI5
const oControl = window.wdi5.getUI5CtlForWebObj(webElement)

if (Array.isArray(result)) {
if (result.length === 0) {
resolve({ status: 0, result: result, returnType: "empty" })
} else if (result[0]?.getParent) {
// expect the method call delivers non-primitive results (like getId())
// but delivers a complex/structured type
// -> currenlty, only getAggregation(...) is supported
// read classname eg. sap.m.ComboBox
controlType = oControl.getMetadata()._sClassName
// execute the function
let result = oControl[methodName].apply(oControl, args)
const metadata = oControl.getMetadata()

result = window.wdi5.createControlIdMap(result, controlType)
resolve({ status: 0, result: result, returnType: "aggregation" })
} else {
resolve({ status: 0, result: result, returnType: "result" })
if (Array.isArray(result)) {
if (result.length === 0) {
return { status: 0, result: result, returnType: "empty" }
} else if (result[0]?.getParent) {
// expect the method call delivers non-primitive results (like getId())
// but delivers a complex/structured type
// -> currenlty, only getAggregation(...) is supported
// read classname eg. sap.m.ComboBox
controlType = oControl.getMetadata()._sClassName

result = window.wdi5.createControlIdMap(result, controlType)
return { status: 0, result: result, returnType: "aggregation" }
} else {
return { status: 0, result: result, returnType: "result" }
}
} else {
// ui5 api <control>.focus() doesn't have return value
if (methodName === "focus" && result === undefined) {
return {
status: 0,
result: `called focus() on wdi5 representation of a ${metadata.getElementName()}`,
returnType: "element"
}
} else if (methodName === "exec" && result && result.status > 0) {
return {
status: result.status,
message: result.message
}
} else if (result === undefined || result === null) {
return {
status: 1,
result: `function ${methodName} does not exist on control ${metadata.getElementName()}!`,
returnType: "none"
}
} else {
if (window.wdi5.isPrimitive(result)) {
return { status: 0, result: result, returnType: "result" }
} else if (
// we have an object that is not a UI5 control
typeof result === "object" &&
result !== null &&
!(result instanceof sap.ui.core.Control) &&
!(result instanceof sap.ui.core.Item)
) {
// save before manipulate
const uuid = window.wdi5.saveObject(result)

// FIXME: extract, collapse and remove cylic in 1 step
// extract the methods first
const aProtoFunctions = window.wdi5.retrieveControlMethods(result, true)

// flatten the prototype so we have all funcs available
const collapsed = window.wdi5.collapseObject(result)
// exclude cyclic references
const collapsedAndNonCyclic = JSON.parse(
JSON.stringify(collapsed, window.wdi5.getCircularReplacer())
)
// remove all empty Array elements, inlcuding private keys (starting with "_")
const semanticCleanedElements = window.wdi5.removeEmptyElements(collapsedAndNonCyclic)

return {
status: 0,
object: semanticCleanedElements,
returnType: "object",
aProtoFunctions: aProtoFunctions,
uuid: uuid,
nonCircularResultObject: semanticCleanedElements
}
} else if (
typeof result === "object" &&
result !== null &&
// wdi5 returns a wdi5 control if the UI5 api return its control
// allows method chaining
!(result instanceof sap.ui.base.Object)
) {
return {
status: 2,
returnType: "unknown"
}
} else {
// ui5 api <control>.focus() doesn't have return value
if (methodName === "focus" && result === undefined) {
resolve({
// we got ourselves a regular UI5 control
// check that we're not working against ourselves :)
if (result && result.getId && oControl.getId() !== result.getId()) {
// ui5 function like get parent might return another ui5 control -> return it to check with this wdi5 instance
result = window.wdi5.createControlId(result)
return { status: 0, result: result, returnType: "newElement" }
} else {
return {
status: 0,
result: `called focus() on wdi5 representation of a ${metadata.getElementName()}`,
result: `instance of wdi5 representation of a ${metadata.getElementName()}`,
returnType: "element"
})
} else if (methodName === "exec" && result && result.status > 0) {
resolve({
status: result.status,
message: result.message
})
} else if (result === undefined || result === null) {
resolve({
status: 1,
result: `function ${methodName} does not exist on control ${metadata.getElementName()}!`,
returnType: "none"
})
} else {
if (window.wdi5.isPrimitive(result)) {
resolve({ status: 0, result: result, returnType: "result" })
} else if (
// we have an object that is not a UI5 control
typeof result === "object" &&
result !== null &&
!(result instanceof sap.ui.core.Control) &&
!(result instanceof sap.ui.core.Item)
) {
// save before manipulate
const uuid = window.wdi5.saveObject(result)

// FIXME: extract, collapse and remove cylic in 1 step
// extract the methods first
const aProtoFunctions = window.wdi5.retrieveControlMethods(result, true)

// flatten the prototype so we have all funcs available
const collapsed = window.wdi5.collapseObject(result)
// exclude cyclic references
const collapsedAndNonCyclic = JSON.parse(
JSON.stringify(collapsed, window.wdi5.getCircularReplacer())
)
// remove all empty Array elements, inlcuding private keys (starting with "_")
const semanticCleanedElements =
window.wdi5.removeEmptyElements(collapsedAndNonCyclic)

resolve({
status: 0,
object: semanticCleanedElements,
returnType: "object",
aProtoFunctions: aProtoFunctions,
uuid: uuid,
nonCircularResultObject: semanticCleanedElements
})
} else if (
typeof result === "object" &&
result !== null &&
// wdi5 returns a wdi5 control if the UI5 api return its control
// allows method chaining
!(result instanceof sap.ui.base.Object)
) {
resolve({
status: 2,
returnType: "unknown"
})
} else {
// we got ourselves a regular UI5 control
// check that we're not working against ourselves :)
if (result && result.getId && oControl.getId() !== result.getId()) {
// ui5 function like get parent might return another ui5 control -> return it to check with this wdi5 instance
result = window.wdi5.createControlId(result)
resolve({ status: 0, result: result, returnType: "newElement" })
} else {
resolve({
status: 0,
result: `instance of wdi5 representation of a ${metadata.getElementName()}`,
returnType: "element"
})
}
}
}
}
},
window.wdi5.errorHandling.bind(this, null, reject)
)
})
}
}
} catch (error) {
window.wdi5.errorHandling.bind(error)
}
},
webElement,
methodName,
Expand Down
102 changes: 48 additions & 54 deletions client-side-js/executeObjectMethod.cjs
Original file line number Diff line number Diff line change
@@ -1,65 +1,59 @@
async function clientSide_executeObjectMethod(uuid, methodName, args) {
return await browser.execute(
async (uuid, methodName, args) => {
return new Promise(async (resolve, reject) => {
await window.wdi5.waitForUI5(
window.wdi5.waitForUI5Options,
// this callback is denoted "async" even though it is truely not
// but what other way to `await` a potentially async UI5 managed object fn in here?
async () => {
// DOM to UI5
const oObject = window.wdi5.objectMap[uuid]
try {
await window.bridge.waitForUI5(window.wdi5.waitForUI5Options)

// execute the function
// TODO: if (methodName === "getName") { debugger }
let result
let threw = false
let threwMessage = ""
if (oObject[methodName].constructor.name === "AsyncFunction") {
try {
result = await oObject[methodName].apply(oObject, args)
} catch (error) {
threw = true
threwMessage = JSON.stringify(error)
window.wdi5.Log.error(threwMessage)
}
} else {
result = oObject[methodName].apply(oObject, args)
}
// DOM to UI5
const oObject = window.wdi5.objectMap[uuid]

// async message call rejected
if (threw) {
resolve({ status: 1, message: threwMessage })
}
// result mus be a primitive
else if (window.wdi5.isPrimitive(result)) {
// getter
resolve({ status: 0, result: result, returnType: "result" })
} else {
// create new object
const uuid = window.wdi5.saveObject(result)
const aProtoFunctions = window.wdi5.retrieveControlMethods(result, true)
// execute the function
// TODO: if (methodName === "getName") { debugger }
let result
let threw = false
let threwMessage = ""
if (oObject[methodName].constructor.name === "AsyncFunction") {
try {
result = await oObject[methodName].apply(oObject, args)
} catch (error) {
threw = true
threwMessage = JSON.stringify(error)
window.wdi5.Log.error(threwMessage)
}
} else {
result = oObject[methodName].apply(oObject, args)
}

result = window.wdi5.collapseObject(result)
// async message call rejected
if (threw) {
return { status: 1, message: threwMessage }
}
// result mus be a primitive
else if (window.wdi5.isPrimitive(result)) {
// getter
return { status: 0, result: result, returnType: "result" }
} else {
// create new object
const uuid = window.wdi5.saveObject(result)
const aProtoFunctions = window.wdi5.retrieveControlMethods(result, true)

const collapsedAndNonCyclic = JSON.parse(
JSON.stringify(result, window.wdi5.getCircularReplacer())
)
// remove all empty Array elements, inlcuding private keys (starting with "_")
const semanticCleanedElements = window.wdi5.removeEmptyElements(collapsedAndNonCyclic)
result = window.wdi5.collapseObject(result)

resolve({
status: 0,
object: semanticCleanedElements,
uuid: uuid,
returnType: "object",
aProtoFunctions: aProtoFunctions
})
}
},
window.wdi5.errorHandling.bind(this, null, reject)
)
})
const collapsedAndNonCyclic = JSON.parse(JSON.stringify(result, window.wdi5.getCircularReplacer()))
// remove all empty Array elements, inlcuding private keys (starting with "_")
const semanticCleanedElements = window.wdi5.removeEmptyElements(collapsedAndNonCyclic)

return {
status: 0,
object: semanticCleanedElements,
uuid: uuid,
returnType: "object",
aProtoFunctions: aProtoFunctions
}
}
} catch (error) {
window.wdi5.errorHandling(error)
}
},
uuid,
methodName,
Expand Down
2 changes: 1 addition & 1 deletion client-side-js/fireEvent.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ async function clientSide_fireEvent(webElement, eventName, oOptions, browserInst
return await browserInstance.execute(
async (webElement, eventName, oOptions) => {
try {
await window.wdi5.waitForUI5(window.wdi5.waitForUI5Options)
await window.bridge.waitForUI5(window.wdi5.waitForUI5Options)
} catch (error) {
return window.wdi5.errorHandling(error)
}
Expand Down
Loading
Loading