Skip to content
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: 2 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ The `forceSelect` (default: `false`) property can be set to `true` to force `wdi

The `forceSelect` option also updates the `wdio` control reference each time a method is executed on a `wdi5` control.

?> When `searchOpenDialogs` is set in the selector and `forceSelect` is not explicitly provided, `wdi5` will automatically bypass the internal control cache for that selector. This ensures a fresh control lookup each time `browser.asControl()` is called, which is necessary because UI5 dialogs destroy and recreate their DOM on close/open. Note that this does not enable the per-method re-retrieval behavior of explicit `forceSelect: true`.

The `timeout` option (default based on the global configuration `waitForUI5Timeout` [setting](wdio-ui5-service/README.md#installation)) controls the maximum waiting time while checking for UI5 availability _(meaning no pending requests / promises / timeouts)_.

The `logging` (default: `true`) property can be set to `false` to disable the log for this specific selector. This can be useful when you want to assert, that specific controls should not be visible on the UI to decrease the amount of pointless error messages.
Expand Down
37 changes: 37 additions & 0 deletions examples/wdio-classic/ui5.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,43 @@ describe("ui5 basic", () => {
expect(isOpen).toBeFalsy()
})

// NOTE: no forceSelect set — wdi5 should auto-bypass cache because searchOpenDialogs is present
it("should bypass cache for searchOpenDialogs on dialog reopen", async () => {
const filterButtonSelector = {
selector: {
id: "container-orderbrowser---master--filterButton",
viewName: "sap.ui.demo.orderbrowser.view.Master"
}
}
const dialogOkButtonSelector = {
selector: {
id: "container-orderbrowser---master--viewSettingsDialog-acceptbutton",
searchOpenDialogs: true,
interaction: {
idSuffix: "BDI-content"
}
}
}

// first open
await browser.asControl(filterButtonSelector).press()
const dialogButton = await browser.asControl(dialogOkButtonSelector)
expect(dialogButton.isInitialized()).toBeTruthy()
expect(await dialogButton.isActive()).toBeTruthy()

// close the dialog
await dialogButton.press()

// reopen — without auto cache bypass, this would fail due to stale cached reference
await browser.asControl(filterButtonSelector).press()
const dialogButton2 = await browser.asControl(dialogOkButtonSelector)
expect(dialogButton2.isInitialized()).toBeTruthy()
expect(await dialogButton2.isActive()).toBeTruthy()

// close again
await dialogButton2.press()
})

it("wdi5 should search and return no results", async () => {
const selector1 = {
selector: {
Expand Down
29 changes: 27 additions & 2 deletions src/lib/wdi5-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,23 @@ export async function _addWdi5Commands(browserInstance: WebdriverIO.Browser) {
return "ERROR: Specified selector is not valid -> abort"
}

// When searchOpenDialogs is set and forceSelect is not explicitly provided by the user,
// bypass the control cache (so each asControl() call fetches fresh from browser),
// but do NOT propagate forceSelect to the control instance (which would cause per-method
// re-retrieval and timeout when the dialog is closed and the element no longer exists).
const skipCache = !!(wdi5Selector.selector?.searchOpenDialogs && wdi5Selector.forceSelect === undefined)
if (skipCache) {
Logger.info(`bypassing cache for selector (searchOpenDialogs is set)`)
}

const internalKey = wdi5Selector.wdio_ui5_key || _createWdioUI5KeyFromSelector(wdi5Selector)
// either retrieve and cache a UI5 control
// or return a cached version
if (!browserInstance._controls?.[internalKey] || wdi5Selector.forceSelect /* always retrieve control */) {
if (
!browserInstance._controls?.[internalKey] ||
wdi5Selector.forceSelect ||
skipCache /* always retrieve control */
) {
Logger.info(`creating internal control with id ${internalKey}`)
wdi5Selector.wdio_ui5_key = internalKey

Expand Down Expand Up @@ -311,8 +324,20 @@ export async function _addWdi5Commands(browserInstance: WebdriverIO.Browser) {
}

const internalKey = wdi5Selector.wdio_ui5_key || _createWdioUI5KeyFromSelector(wdi5Selector)

// When searchOpenDialogs is set and forceSelect is not explicitly provided by the user,
// bypass the control cache but do NOT propagate forceSelect to control instances.
const skipCache = !!(wdi5Selector.selector?.searchOpenDialogs && wdi5Selector.forceSelect === undefined)
if (skipCache) {
Logger.info(`bypassing cache for allControls selector (searchOpenDialogs is set)`)
}

// REVISIT all elements receive the same! internal key
if (!browserInstance._controls?.[internalKey] || wdi5Selector.forceSelect /* always retrieve control */) {
if (
!browserInstance._controls?.[internalKey] ||
wdi5Selector.forceSelect ||
skipCache /* always retrieve control */
) {
wdi5Selector.wdio_ui5_key = internalKey
Logger.info(`creating internal controls with id ${internalKey}`)
browserInstance._controls[internalKey] = await _allControls(wdi5Selector, browserInstance)
Expand Down
1 change: 1 addition & 0 deletions src/lib/wdi5-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export class WDI5Control {
this._controlSelector = controlSelector
this._wdio_ui5_key = controlSelector?.wdio_ui5_key
this._forceSelect = forceSelect

this._logging = this._controlSelector?.logging ?? true

const controlResult = await this._getControl()
Expand Down
Loading