From 484d4e152d1927289de58967b2e5a7611efe3b0e Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Mon, 5 Oct 2026 17:47:01 +0530 Subject: [PATCH 1/3] fix(cli): drop a log written before the first mocha hook instead of logging an ERROR (SDK-7843) Console output from wdio's `before`/`beforeSession` hooks reaches trackEvent(LOG, POST) before mocha's first hook, so no TestFramework instance exists and resolveInstance cannot create one for LOG. Since 9.30.0 that printed "resolveInstance: unable to resolve/create instance" and "trackEvent: instance not found" at ERROR level on every worker. Drop it at debug level instead, matching the classic path, which already ignores a log that has no hook or test uuid. Co-Authored-By: Claude Opus 5.5 --- .../cli/frameworks/wdioMochaTestFramework.ts | 9 +++ .../wdioMochaTestFramework.preTestLog.test.ts | 56 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 packages/browserstack-service/tests/cli/wdioMochaTestFramework.preTestLog.test.ts diff --git a/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts b/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts index b5706ee2..23b203d5 100644 --- a/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts +++ b/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts @@ -37,6 +37,15 @@ export default class WdioMochaTestFramework extends TestFramework { logger.info(`trackEvent: testFrameworkState=${testFrameworkState} hookState=${hookState}`) await super.trackEvent(testFrameworkState, hookState, args) + // Console output from wdio's `before`/`beforeSession` hooks arrives before mocha's first + // hook, so there is no test or hook to attach it to yet and resolveInstance cannot create + // one for LOG. The classic path drops such a log silently; do the same instead of + // printing an ERROR on every worker (SDK-7843). + if (testFrameworkState === TestFrameworkState.LOG && !TestFramework.getTrackedInstance()) { + logger.debug(`trackEvent: no test or hook started yet, dropping log for testFrameworkState=${testFrameworkState} hookState=${hookState}`) + return + } + const instance = this.resolveInstance(testFrameworkState, hookState, args) if (instance === null) { logger.error(`trackEvent: instance not found for testFrameworkState=${testFrameworkState} hookState=${hookState}`) diff --git a/packages/browserstack-service/tests/cli/wdioMochaTestFramework.preTestLog.test.ts b/packages/browserstack-service/tests/cli/wdioMochaTestFramework.preTestLog.test.ts new file mode 100644 index 00000000..1ee5a019 --- /dev/null +++ b/packages/browserstack-service/tests/cli/wdioMochaTestFramework.preTestLog.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import * as bstackLogger from '../../src/bstackLogger.js' + +import WdioMochaTestFramework from '../../src/cli/frameworks/wdioMochaTestFramework.js' +import TestFramework from '../../src/cli/frameworks/testFramework.js' +import { TestFrameworkState } from '../../src/cli/states/testFrameworkState.js' +import { HookState } from '../../src/cli/states/hookState.js' +import { BStackLogger as cliLogger } from '../../src/cli/cliLogger.js' + +vi.spyOn(bstackLogger.BStackLogger, 'logToFile').mockImplementation(() => {}) + +describe('SDK-7843 — a log written before the first mocha hook is dropped quietly', () => { + let framework: WdioMochaTestFramework + let errorSpy: ReturnType + + beforeEach(() => { + framework = new WdioMochaTestFramework(['WebdriverIO', 'mocha'], {}, 'bin-session-id') + errorSpy = vi.spyOn(cliLogger, 'error').mockImplementation(() => {}) + vi.spyOn(cliLogger, 'info').mockImplementation(() => {}) + vi.spyOn(cliLogger, 'debug').mockImplementation(() => {}) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('does not log an ERROR for console output from wdio\'s `before` hook', async () => { + // wdio's `before` runs before mocha's `before all`, so no instance is tracked yet. + vi.spyOn(TestFramework, 'getTrackedInstance').mockReturnValue(null as any) + const resolveSpy = vi.spyOn(framework, 'resolveInstance') + + await framework.trackEvent(TestFrameworkState.LOG, HookState.POST, { + logEntry: { kind: 'TEST_LOG', message: '[SelfHealer] Installed', level: 'info', timestamp: new Date().toISOString() } + }) + + expect(errorSpy).not.toHaveBeenCalled() + expect(resolveSpy).not.toHaveBeenCalled() + }) + + it('still resolves the instance for a log once a test or hook is tracked', async () => { + vi.spyOn(TestFramework, 'getTrackedInstance').mockReturnValue({} as any) + const resolveSpy = vi.spyOn(framework, 'resolveInstance').mockReturnValue(null) + + await framework.trackEvent(TestFrameworkState.LOG, HookState.POST, { logEntry: {} }) + + expect(resolveSpy).toHaveBeenCalledOnce() + }) + + it('still reports a missing instance for non-log events', async () => { + vi.spyOn(TestFramework, 'getTrackedInstance').mockReturnValue(null as any) + + await framework.trackEvent(TestFrameworkState.TEST, HookState.POST, {}) + + expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('resolveInstance: unable to resolve/create instance')) + }) +}) From c74564c40c03760057f0d6ad8995d3fb6291759d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:20:04 +0000 Subject: [PATCH 2/3] chore(changeset): auto-generate from PR template (patch) --- .changeset/pr-265.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/pr-265.md diff --git a/.changeset/pr-265.md b/.changeset/pr-265.md new file mode 100644 index 00000000..506bdeb2 --- /dev/null +++ b/.changeset/pr-265.md @@ -0,0 +1,5 @@ +--- +"@wdio/browserstack-service": patch +--- + +- Removed misleading `resolveInstance: unable to resolve/create instance ... TestFrameworkState.LOG` / `trackEvent: instance not found` error messages. They were printed when a wdio `before` hook writes to the console. Test execution and reporting were never affected. From 18f5f6112fec9c53c1f1c574d2f8556009b4bb7f Mon Sep 17 00:00:00 2001 From: Aakash Hotchandani Date: Mon, 5 Oct 2026 22:38:20 +0530 Subject: [PATCH 3/3] docs(cli): name only the wdio `before` hook in the pre-test log guard comment (SDK-7843) `beforeSession` runs before the service's own before() patches console, so its output never reaches appendTestItemLog. Co-Authored-By: Claude Opus 5.5 --- .../src/cli/frameworks/wdioMochaTestFramework.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts b/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts index 23b203d5..a4457a06 100644 --- a/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts +++ b/packages/browserstack-service/src/cli/frameworks/wdioMochaTestFramework.ts @@ -37,9 +37,9 @@ export default class WdioMochaTestFramework extends TestFramework { logger.info(`trackEvent: testFrameworkState=${testFrameworkState} hookState=${hookState}`) await super.trackEvent(testFrameworkState, hookState, args) - // Console output from wdio's `before`/`beforeSession` hooks arrives before mocha's first - // hook, so there is no test or hook to attach it to yet and resolveInstance cannot create - // one for LOG. The classic path drops such a log silently; do the same instead of + // Console output from wdio's `before` hook (after the service has patched console) + // arrives before mocha's first hook, so there is no test or hook to attach it to yet and + // resolveInstance cannot create one for LOG. The classic path drops such a log silently; do the same instead of // printing an ERROR on every worker (SDK-7843). if (testFrameworkState === TestFrameworkState.LOG && !TestFramework.getTrackedInstance()) { logger.debug(`trackEvent: no test or hook started yet, dropping log for testFrameworkState=${testFrameworkState} hookState=${hookState}`)