From 9fe32a8d47b07b2dcef6a1c69894687f9b2ad7d9 Mon Sep 17 00:00:00 2001
From: P-Courteille
Date: Sun, 13 Jul 2025 12:00:57 +0200
Subject: [PATCH] issue_657_and_983
---
.changeset/spotty-bananas-glow.md | 9 +++++
packages/ocr-service/src/service.ts | 39 +++++++++++--------
packages/visual-reporter/package.json | 2 +-
packages/visual-service/src/service.ts | 27 +++++++------
packages/visual-service/src/utils.ts | 5 ++-
.../src/methods/methods.interfaces.ts | 12 +++---
6 files changed, 54 insertions(+), 40 deletions(-)
create mode 100644 .changeset/spotty-bananas-glow.md
diff --git a/.changeset/spotty-bananas-glow.md b/.changeset/spotty-bananas-glow.md
new file mode 100644
index 00000000..fd3370f2
--- /dev/null
+++ b/.changeset/spotty-bananas-glow.md
@@ -0,0 +1,9 @@
+---
+"@wdio/ocr-service": minor
+---
+
+\- improve typing for executor (as previously done in webdriverio package)
+
+\- For both ocr-service and visual-service, the functions are now first add to the browser object and then to each instances to avoid having the loop for each instance in the command of a single instance (function with the loop no longer call itself like in the issue 657 and are now correctly declared to the correct element to fix issue 983)
+
+\- Update the clean script for visual-reporter to work with Windows
\ No newline at end of file
diff --git a/packages/ocr-service/src/service.ts b/packages/ocr-service/src/service.ts
index e9aa408a..49d5a83d 100644
--- a/packages/ocr-service/src/service.ts
+++ b/packages/ocr-service/src/service.ts
@@ -24,11 +24,14 @@ export default class WdioOcrService {
private _ocrDir: string
private _ocrLanguage: string
private _ocrContrast: number
+ private _isTesseractAvailable: boolean
constructor(options: OcrOptions) {
this._ocrDir = createOcrDir(options?.imagesFolder || DEFAULT_IMAGES_FOLDER)
this._ocrLanguage = options?.language || SUPPORTED_LANGUAGES.ENGLISH
this._ocrContrast = options?.contrast || CONTRAST
+ this._ocrLanguage = options?.language || SUPPORTED_LANGUAGES.ENGLISH
+ this._isTesseractAvailable = isSystemTesseractAvailable()
}
/**
@@ -58,24 +61,19 @@ export default class WdioOcrService {
const browserNames = Object.keys(capabilities)
const self = this
log.info(`Adding commands to Multi Browser: ${browserNames.join(', ')}`)
-
- for (const browserName of browserNames) {
- const multiremoteBrowser = browser as WebdriverIO.MultiRemoteBrowser
- const browserInstance = multiremoteBrowser.getInstance(browserName)
- await this.#addCommandsToBrowser(browserInstance)
- }
-
/**
- * Add all OCR commands to the global browser object that will execute
- * on each browser in the Multi Remote.
+ * Add all commands to the global browser object that will execute on each browser in the Multi Remote.
*/
- for (const command of Object.keys(ocrCommands)) {
- browser.addCommand(command, async function (...args: unknown[]) {
+ for (const commandName of Object.keys(ocrCommands)) {
+ browser.addCommand(commandName, async function (...args: unknown[]) {
const returnData: Record = {}
if (typeof args[0] === 'object' && args[0] !== null) {
const options = args[0] as Record
+ options.ocrImagesPath = options?.imagesFolder || self._ocrDir
options.contrast = options?.contrast || self._ocrContrast
+ options.language = options?.language || self._ocrLanguage
+ options.isTesseractAvailable = self._isTesseractAvailable
args[0] = options
}
@@ -83,20 +81,27 @@ export default class WdioOcrService {
const multiremoteBrowser = browser as WebdriverIO.MultiRemoteBrowser
const browserInstance = multiremoteBrowser.getInstance(browserName) as WebdriverIO.Browser & Record
- if (typeof browserInstance[command] === 'function') {
- returnData[browserName] = await browserInstance[command].apply(browserInstance, args)
+ if (typeof browserInstance[commandName] === 'function') {
+ returnData[browserName] = await browserInstance[commandName].apply(browserInstance, args)
} else {
- throw new Error(`Command ${command} is not a function on the browser instance ${browserName}`)
+ throw new Error(`Command ${commandName} is not a function on the browser instance ${browserName}`)
}
}
return returnData
})
}
+ /**
+ * Add all commands to each instance (but Single Remote version)
+ */
+ for (const browserName of browserNames) {
+ const multiremoteBrowser = browser as WebdriverIO.MultiRemoteBrowser
+ const browserInstance = multiremoteBrowser.getInstance(browserName)
+ await this.#addCommandsToBrowser(browserInstance)
+ }
}
async #addCommandsToBrowser(currentBrowser: WebdriverIO.Browser) {
- const isTesseractAvailable = isSystemTesseractAvailable()
const self = this
for (const [commandName, command] of Object.entries(ocrCommands)) {
@@ -106,10 +111,10 @@ export default class WdioOcrService {
function (this: typeof currentBrowser, options) {
return command.bind(this)({
...options,
+ ocrImagesPath: self._ocrDir,
contrast: options?.contrast || self._ocrContrast,
- isTesseractAvailable,
language: options?.language || self._ocrLanguage,
- ocrImagesPath: self._ocrDir,
+ isTesseractAvailable: self._isTesseractAvailable
})
}
)
diff --git a/packages/visual-reporter/package.json b/packages/visual-reporter/package.json
index 792d2e34..5b35d4ef 100644
--- a/packages/visual-reporter/package.json
+++ b/packages/visual-reporter/package.json
@@ -17,7 +17,7 @@
"build": "run-s clean build:*",
"build:report": "remix vite:build",
"build:scripts": "tsc -p tsconfig.scripts.json",
- "clean": "rimraf coverage build *.tsbuildinfo",
+ "clean": "rimraf coverage build --glob *.tsbuildinfo",
"dev": "cross-env VISUAL_REPORT_LOCAL_DEV=true run-s build:scripts script:prepare.report && run-p watch:scripts dev:remix",
"dev:remix": "remix vite:dev",
"script:prepare.report": "node ./dist/prepareReportAssets.js",
diff --git a/packages/visual-service/src/service.ts b/packages/visual-service/src/service.ts
index 2a1bf723..a8fece7f 100644
--- a/packages/visual-service/src/service.ts
+++ b/packages/visual-service/src/service.ts
@@ -2,6 +2,7 @@ import logger from '@wdio/logger'
import { expect } from '@wdio/globals'
import { dirname, normalize, resolve } from 'node:path'
import type { Capabilities, Frameworks } from '@wdio/types'
+import type { TransformElement } from 'webdriverio'
import {
BaseClass,
checkElement,
@@ -142,7 +143,14 @@ export default class WdioImageComparisonService extends BaseClass {
const browserNames = Object.keys(capabilities)
/**
- * Add all the commands to each browser in the Multi Remote
+ * Add all commands to the global browser object that will execute on each browser in the Multi Remote.
+ */
+ for (const [commandName, command] of Object.entries(pageCommands)) {
+ this.#addMultiremoteCommand(browser, browserNames, commandName, command)
+ }
+
+ /**
+ * Add all commands to each instance (but Single Remote version)
*/
for (const browserName of browserNames) {
log.info(`Adding commands to Multi Browser: ${browserName}`)
@@ -155,15 +163,6 @@ export default class WdioImageComparisonService extends BaseClass {
await this.#addCommandsToBrowser(browserInstance)
}
- /**
- * Add all the commands to the global browser object that will execute
- * on each browser in the Multi Remote
- * Start with the page commands
- */
- for (const [commandName, command] of Object.entries(pageCommands)) {
- this.#addMultiremoteCommand(browser, browserNames, commandName, command)
- }
-
/**
* Add all the element commands to the global browser object that will execute
* on each browser in the Multi Remote
@@ -234,7 +233,7 @@ export default class WdioImageComparisonService extends BaseClass {
methods: {
bidiScreenshot: isBiDiScreenshotSupported(browser) ? this.browsingContextCaptureScreenshot.bind(browser) : undefined,
executor: (
- fn: string | ((...args: InnerArguments) => ReturnValue),
+ fn: string | ((...innerArgs: TransformElement) => ReturnValue),
...args: InnerArguments
): Promise => {
return this.execute(fn, ...args) as Promise
@@ -311,7 +310,7 @@ export default class WdioImageComparisonService extends BaseClass {
methods: {
bidiScreenshot: isBiDiScreenshotSupported(browser) ? this.browsingContextCaptureScreenshot.bind(browser) : undefined,
executor: (
- fn: string | ((...args: InnerArguments) => ReturnValue),
+ fn: string | ((...innerArgs: TransformElement) => ReturnValue),
...args: InnerArguments
): Promise => {
return this.execute(fn, ...args) as Promise
@@ -387,7 +386,7 @@ export default class WdioImageComparisonService extends BaseClass {
methods: {
bidiScreenshot: isBiDiScreenshotSupported(browserInstance) ? browserInstance.browsingContextCaptureScreenshot.bind(browserInstance) : undefined,
executor: (
- fn: string | ((...args: InnerArguments) => ReturnValue),
+ fn: string | ((...innerArgs: TransformElement) => ReturnValue),
...args: InnerArguments
): Promise => {
return browserInstance.execute(fn, ...args) as Promise
@@ -480,7 +479,7 @@ export default class WdioImageComparisonService extends BaseClass {
methods: {
bidiScreenshot: isBiDiScreenshotSupported(browserInstance) ? browserInstance.browsingContextCaptureScreenshot.bind(browserInstance) : undefined,
executor: (
- fn: string | ((...args: InnerArguments) => ReturnValue),
+ fn: string | ((...innerArgs: TransformElement) => ReturnValue),
...args: InnerArguments
): Promise => {
return browserInstance.execute(fn, ...args) as Promise
diff --git a/packages/visual-service/src/utils.ts b/packages/visual-service/src/utils.ts
index 2563e49a..f34eb79b 100644
--- a/packages/visual-service/src/utils.ts
+++ b/packages/visual-service/src/utils.ts
@@ -2,6 +2,7 @@ import type { Capabilities } from '@wdio/types'
import type { AppiumCapabilities } from 'node_modules/@wdio/types/build/Capabilities.js'
import { getMobileScreenSize, getMobileViewPortPosition, IOS_OFFSETS, NOT_KNOWN } from 'webdriver-image-comparison'
import type { Folders, InstanceData, TestContext } from 'webdriver-image-comparison'
+import type { TransformElement, TransformReturn } from 'webdriverio'
import type {
EnrichTestContextOptions,
getFolderMethodOptions,
@@ -70,8 +71,8 @@ async function getMobileInstanceData({
if (isMobile) {
const executor = (
- fn: string | ((...args: InnerArguments) => ReturnValue),
- ...args: InnerArguments) => currentBrowser.execute(fn, ...args) as Promise
+ fn: string | ((...innerArgs: TransformElement) => ReturnValue),
+ ...args: InnerArguments) => currentBrowser.execute(fn, ...args) as Promise>
const getUrl = () => currentBrowser.getUrl()
const url = (arg:string) => currentBrowser.url(arg)
const currentDriverCapabilities = currentBrowser.capabilities
diff --git a/packages/webdriver-image-comparison/src/methods/methods.interfaces.ts b/packages/webdriver-image-comparison/src/methods/methods.interfaces.ts
index 38ad4a2e..a36ad4dd 100644
--- a/packages/webdriver-image-comparison/src/methods/methods.interfaces.ts
+++ b/packages/webdriver-image-comparison/src/methods/methods.interfaces.ts
@@ -1,15 +1,15 @@
import type { RectanglesOutput } from './rectangles.interfaces.js'
-
+import type { TransformReturn, TransformElement } from 'webdriverio'
// There a multiple ways to call the executor method, for mobile and web
-type ExecuteScript = (
- fn: (...args: Args) => ReturnValue,
- ...args: Args
- ) => Promise;
+type ExecuteScript = (
+ fn: (...innerArgs: TransformElement) => ReturnValue,
+ ...args: InnerArguments
+ ) => Promise>;
type ExecuteMobile = (
fn: string,
args?: Record
-) => Promise;
+) => Promise>;
interface BrowsingContextCaptureScreenshotParameters {
context: string;
origin?: 'viewport' | 'document';