From f1fbf6448fe2aace64ee05502fdffbfff1aeff75 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Mon, 15 Sep 2025 18:22:57 +0530 Subject: [PATCH 01/22] added changes for targeted accessibility scans --- .../src/cli/modules/accessibilityModule.ts | 96 ++++++++++++++++--- 1 file changed, 81 insertions(+), 15 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 754aed7c7db..d9bd6a1ed7b 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -1,5 +1,6 @@ /// import BaseModule from './baseModule.js' +import { BrowserstackCLI } from '../index.js'; import { BStackLogger } from '../cliLogger.js' import TestFramework from '../frameworks/testFramework.js' import AutomationFramework from '../frameworks/automationFramework.js' @@ -9,7 +10,7 @@ import { TestFrameworkState } from '../states/testFrameworkState.js' import { AutomationFrameworkState } from '../states/automationFrameworkState.js' import { HookState } from '../states/hookState.js' import accessibilityScripts from '../../scripts/accessibility-scripts.js' -import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, validateCapsWithNonBstackA11y } from '../../util.js' +import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, validateCapsWithNonBstackA11y, isBrowserstackSession } from '../../util.js' import { AutomationFrameworkConstants } from '../frameworks/constants/automationFrameworkConstants.js' import util from 'node:util' import type { Accessibility } from '@browserstack/wdio-browserstack-service' @@ -25,12 +26,14 @@ export default class AccessibilityModule extends BaseModule { name: string scriptInstance: typeof accessibilityScripts accessibility: boolean = false + autoScanning: boolean = true isAppAccessibility: boolean isNonBstackA11y: boolean accessibilityConfig: Accessibility static MODULE_NAME = 'AccessibilityModule' - accessibilityMap: Map - LOG_DISABLED_SHOWN: Map + accessibilityMap: Map + LOG_DISABLED_SHOWN: Map + testMetadata: Record = {} constructor(accessibilityConfig: Accessibility, isNonBstackA11y: boolean) { super() @@ -39,7 +42,9 @@ export default class AccessibilityModule extends BaseModule { AutomationFramework.registerObserver(AutomationFrameworkState.CREATE, HookState.POST, this.onBeforeExecute.bind(this)) TestFramework.registerObserver(TestFrameworkState.TEST, HookState.PRE, this.onBeforeTest.bind(this)) TestFramework.registerObserver(TestFrameworkState.TEST, HookState.POST, this.onAfterTest.bind(this)) - this.accessibility = true + this.accessibility = Boolean(accessibilityConfig) + const accessibilityOptions = (BrowserstackCLI.getInstance().options as any)?.accessibilityOptions + this.autoScanning = accessibilityOptions?.autoScanning ?? true; this.scriptInstance = accessibilityScripts this.accessibilityMap = new Map() this.LOG_DISABLED_SHOWN = new Map() @@ -50,6 +55,8 @@ export default class AccessibilityModule extends BaseModule { async onBeforeExecute() { try { const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() + const testInstance: TestFrameworkInstance = TestFramework.getTrackedInstance() + if (!autoInstance) { this.logger.debug('No tracked instances found!') @@ -106,6 +113,15 @@ export default class AccessibilityModule extends BaseModule { return await this.performScanCli(browser) } + (browser as any).startA11yScanning = async () => { + this.logger.warn(`Accessibility scanning cannot be started from outside the test`) + } + + (browser as any).stopA11yScanning = async () => { + this.logger.warn(`Accessibility scanning cannot be stopped from outside the test`) + } + + if (!this.accessibility) { this.logger.info('Accessibility automation is disabled for this session.') return @@ -178,18 +194,51 @@ export default class AccessibilityModule extends BaseModule { const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID) const accessibilityOptions = this.config.accessibilityOptions - const shouldScanTest = shouldScanTestForAccessibility(suiteTitle, test.title, accessibilityOptions as { [key: string]: any } | undefined) && this.accessibility - + const shouldScanTest = this.autoScanning && shouldScanTestForAccessibility(suiteTitle, test.title, accessibilityOptions as { [key: string]: any } | undefined) && this.accessibility + + this.accessibilityMap.set(sessionId, shouldScanTest) // Create test metadata similar to accessibility-handler - const testIdentifier = testInstance.getContext().getId() - const testMetadata = { + const testIdentifier = String(testInstance.getContext().getId()) + this.testMetadata[testIdentifier] = { scanTestForAccessibility: shouldScanTest, accessibilityScanStarted: shouldScanTest } + const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser + + (browser as any).startA11yScanning = async () => { + this.accessibilityMap.set(sessionId, true) + this.testMetadata[testIdentifier] = { + scanTestForAccessibility : true, + accessibilityScanStarted : true + } + TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, this.testMetadata[testIdentifier]) + await this._setAnnotation('Accessibility scanning has started') + } + + (browser as any).stopA11yScanning = async () => { + this.accessibilityMap.set(sessionId, false) + await this._setAnnotation('Accessibility scanning has stopped') + } + + (browser as any).performScan = async () => { + const results = await this.performScanCli(browser) + if(results){ + const testIdentifier = String(testInstance.getContext().getId()) + this.testMetadata[testIdentifier] = { + scanTestForAccessibility : true, + accessibilityScanStarted : true + } + TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, this.testMetadata[testIdentifier]) + await this._setAnnotation('Accessibility scanning was triggered manually') + + } + return results + } + // Store test metadata in test instance - TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, testMetadata) - this.accessibilityMap.set(sessionId, shouldScanTest) + TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, this.testMetadata[testIdentifier]) + // Log if accessibility scan is enabled for this test if (shouldScanTest) { @@ -219,7 +268,7 @@ export default class AccessibilityModule extends BaseModule { } // Get test metadata that was stored in onBeforeTest - const testIdentifier = testInstance.getContext().getId() + const testIdentifier = String(testInstance.getContext().getId()) const testMetadata = testInstance.getData(`accessibility_metadata_${testIdentifier}`) if (!testMetadata) { @@ -335,14 +384,16 @@ export default class AccessibilityModule extends BaseModule { private async sendTestStopEvent(browser: WebdriverIO.Browser, dataForExtension: any) { try { + const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() + const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID) if (!this.accessibility) { this.logger.debug('Not an Accessibility Automation session.') return } - - this.logger.debug('Performing scan before saving results') - await this.performScanCli(browser) - + if (sessionId && this.accessibilityMap.get(sessionId)) { + this.logger.debug('Performing scan before saving results') + await this.performScanCli(browser, 'afterTest') + } if (this.isAppAccessibility) { return } @@ -412,4 +463,19 @@ export default class AccessibilityModule extends BaseModule { return {} } + public async _setAnnotation(message: string) { + const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() + const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser + + if (this.accessibility && isBrowserstackSession(browser)) { + await (browser as WebdriverIO.Browser).execute(`browserstack_executor: ${JSON.stringify({ + action: 'annotate', + arguments: { + data: message, + level: 'info' + } + })}`) + } + } + } From 05346155421c25db6a6963a6d5929a4f03846375 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Mon, 15 Sep 2025 18:23:31 +0530 Subject: [PATCH 02/22] fix: linting errors --- .../src/cli/modules/accessibilityModule.ts | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index d9bd6a1ed7b..822d6dc6880 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -1,6 +1,6 @@ /// import BaseModule from './baseModule.js' -import { BrowserstackCLI } from '../index.js'; +import { BrowserstackCLI } from '../index.js' import { BStackLogger } from '../cliLogger.js' import TestFramework from '../frameworks/testFramework.js' import AutomationFramework from '../frameworks/automationFramework.js' @@ -44,7 +44,7 @@ export default class AccessibilityModule extends BaseModule { TestFramework.registerObserver(TestFrameworkState.TEST, HookState.POST, this.onAfterTest.bind(this)) this.accessibility = Boolean(accessibilityConfig) const accessibilityOptions = (BrowserstackCLI.getInstance().options as any)?.accessibilityOptions - this.autoScanning = accessibilityOptions?.autoScanning ?? true; + this.autoScanning = accessibilityOptions?.autoScanning ?? true this.scriptInstance = accessibilityScripts this.accessibilityMap = new Map() this.LOG_DISABLED_SHOWN = new Map() @@ -55,8 +55,6 @@ export default class AccessibilityModule extends BaseModule { async onBeforeExecute() { try { const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() - const testInstance: TestFrameworkInstance = TestFramework.getTrackedInstance() - if (!autoInstance) { this.logger.debug('No tracked instances found!') @@ -114,14 +112,13 @@ export default class AccessibilityModule extends BaseModule { } (browser as any).startA11yScanning = async () => { - this.logger.warn(`Accessibility scanning cannot be started from outside the test`) + this.logger.warn('Accessibility scanning cannot be started from outside the test') } (browser as any).stopA11yScanning = async () => { - this.logger.warn(`Accessibility scanning cannot be stopped from outside the test`) + this.logger.warn('Accessibility scanning cannot be stopped from outside the test') } - if (!this.accessibility) { this.logger.info('Accessibility automation is disabled for this session.') return @@ -195,7 +192,7 @@ export default class AccessibilityModule extends BaseModule { const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID) const accessibilityOptions = this.config.accessibilityOptions const shouldScanTest = this.autoScanning && shouldScanTestForAccessibility(suiteTitle, test.title, accessibilityOptions as { [key: string]: any } | undefined) && this.accessibility - + this.accessibilityMap.set(sessionId, shouldScanTest) // Create test metadata similar to accessibility-handler const testIdentifier = String(testInstance.getContext().getId()) @@ -223,14 +220,14 @@ export default class AccessibilityModule extends BaseModule { (browser as any).performScan = async () => { const results = await this.performScanCli(browser) - if(results){ + if (results){ const testIdentifier = String(testInstance.getContext().getId()) this.testMetadata[testIdentifier] = { - scanTestForAccessibility : true, - accessibilityScanStarted : true - } - TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, this.testMetadata[testIdentifier]) - await this._setAnnotation('Accessibility scanning was triggered manually') + scanTestForAccessibility : true, + accessibilityScanStarted : true + } + TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, this.testMetadata[testIdentifier]) + await this._setAnnotation('Accessibility scanning was triggered manually') } return results @@ -238,7 +235,6 @@ export default class AccessibilityModule extends BaseModule { // Store test metadata in test instance TestFramework.setState(testInstance, `accessibility_metadata_${testIdentifier}`, this.testMetadata[testIdentifier]) - // Log if accessibility scan is enabled for this test if (shouldScanTest) { @@ -466,7 +462,7 @@ export default class AccessibilityModule extends BaseModule { public async _setAnnotation(message: string) { const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser - + if (this.accessibility && isBrowserstackSession(browser)) { await (browser as WebdriverIO.Browser).execute(`browserstack_executor: ${JSON.stringify({ action: 'annotate', From a3f033e686060544bea0d6628cddd97991557af6 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Mon, 15 Sep 2025 19:33:58 +0530 Subject: [PATCH 03/22] added validation for accessibility caps --- .../src/cli/modules/accessibilityModule.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 822d6dc6880..439fd489656 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -108,14 +108,23 @@ export default class AccessibilityModule extends BaseModule { //patching performScan (browser as any).performScan = async () => { + if (!this.accessibility){ + return + } return await this.performScanCli(browser) } (browser as any).startA11yScanning = async () => { + if (!this.accessibility){ + return + } this.logger.warn('Accessibility scanning cannot be started from outside the test') } (browser as any).stopA11yScanning = async () => { + if (!this.accessibility){ + return + } this.logger.warn('Accessibility scanning cannot be stopped from outside the test') } @@ -182,6 +191,7 @@ export default class AccessibilityModule extends BaseModule { async onBeforeTest(args: any) { try { + this.logger.debug('Accessibility before test hook. Starting accessibility scan for this test case.') const suiteTitle = args.suiteTitle || '' const test = args.test || {} @@ -204,6 +214,9 @@ export default class AccessibilityModule extends BaseModule { const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser (browser as any).startA11yScanning = async () => { + if (!this.accessibility){ + return + } this.accessibilityMap.set(sessionId, true) this.testMetadata[testIdentifier] = { scanTestForAccessibility : true, @@ -214,11 +227,17 @@ export default class AccessibilityModule extends BaseModule { } (browser as any).stopA11yScanning = async () => { + if (!this.accessibility){ + return + } this.accessibilityMap.set(sessionId, false) await this._setAnnotation('Accessibility scanning has stopped') } (browser as any).performScan = async () => { + if (!this.accessibility){ + return + } const results = await this.performScanCli(browser) if (results){ const testIdentifier = String(testInstance.getContext().getId()) @@ -253,7 +272,7 @@ export default class AccessibilityModule extends BaseModule { this.logger.debug('Accessibility after test hook. Before sending test stop event') try { - + const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() const testInstance: TestFrameworkInstance = TestFramework.getTrackedInstance() const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID) From f6696f323b030fd4e5169b0a926f64b2ef71ad2f Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Mon, 15 Sep 2025 19:38:51 +0530 Subject: [PATCH 04/22] fix: linting error --- .../src/cli/modules/accessibilityModule.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 439fd489656..35785df4484 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -108,21 +108,21 @@ export default class AccessibilityModule extends BaseModule { //patching performScan (browser as any).performScan = async () => { - if (!this.accessibility){ + if (!this.accessibility){ return } return await this.performScanCli(browser) } (browser as any).startA11yScanning = async () => { - if (!this.accessibility){ + if (!this.accessibility){ return } this.logger.warn('Accessibility scanning cannot be started from outside the test') } (browser as any).stopA11yScanning = async () => { - if (!this.accessibility){ + if (!this.accessibility){ return } this.logger.warn('Accessibility scanning cannot be stopped from outside the test') @@ -272,7 +272,7 @@ export default class AccessibilityModule extends BaseModule { this.logger.debug('Accessibility after test hook. Before sending test stop event') try { - + const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() const testInstance: TestFrameworkInstance = TestFramework.getTrackedInstance() const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID) From ec5cf3d97fe952c0120b50222068333c6e171ea0 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Mon, 15 Sep 2025 20:17:55 +0530 Subject: [PATCH 05/22] Empty commit From 4815141425fc720b02189e2bb09abac9ee315461 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 17:30:42 +0530 Subject: [PATCH 06/22] added warning for targeted scans --- .../src/accessibility-handler.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/wdio-browserstack-service/src/accessibility-handler.ts b/packages/wdio-browserstack-service/src/accessibility-handler.ts index 6682b1d6b1c..c1aead50850 100644 --- a/packages/wdio-browserstack-service/src/accessibility-handler.ts +++ b/packages/wdio-browserstack-service/src/accessibility-handler.ts @@ -212,6 +212,10 @@ class _AccessibilityHandler { } browserWithA11y.startA11yScanning = async () => { + if (this._testIdentifier === null){ + BStackLogger.warn('Accessibility scanning cannot be started from outside the test') + return + } AccessibilityHandler._a11yScanSessionMap[sessionId] = true this._testMetadata[this._testIdentifier as string] = { scanTestForAccessibility : true, @@ -221,6 +225,10 @@ class _AccessibilityHandler { } browserWithA11y.stopA11yScanning = async () => { + if (this._testIdentifier === null){ + BStackLogger.warn('Accessibility scanning cannot be stopped from outside the test') + return + } AccessibilityHandler._a11yScanSessionMap[sessionId] = false await this._setAnnotation('Accessibility scanning has stopped') } From 8b23ad561f2b5317ce18c5daceaac266e6b5965a Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 18:31:27 +0530 Subject: [PATCH 07/22] chore: fixed and added UTs --- .../cli/modules/accessibilityModule.test.ts | 180 ++++++++++++++---- 1 file changed, 148 insertions(+), 32 deletions(-) diff --git a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts index c760225e9a2..60c4608f92e 100644 --- a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -1,13 +1,27 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' -vi.mock('../../../src/cli/frameworks/testFramework.js', () => ({ - default: { - registerObserver: vi.fn(), - getTrackedInstance: vi.fn(), - getState: vi.fn(), - setState: vi.fn() +// Mock TestFramework as a proper class that can be extended +vi.mock('../../../src/cli/frameworks/testFramework.js', () => { + const MockTestFramework = class TestFramework { + static registerObserver = vi.fn() + static getTrackedInstance = vi.fn() + static getState = vi.fn() + static setState = vi.fn() + static setTrackedInstance = vi.fn() + + constructor() {} + + getTestFrameworks() { return ['mocha'] } + getTestFrameworksVersions() { return { mocha: '1.0.0' } } + updateInstanceState() {} + runHooks() {} + trackEvent() {} } -})) + + return { + default: MockTestFramework + } +}) vi.mock('../../../src/cli/frameworks/automationFramework.js', () => ({ default: { @@ -31,11 +45,13 @@ vi.mock('../../../src/scripts/accessibility-scripts.js', () => ({ vi.mock('../../../src/util.js', () => ({ validateCapsWithA11y: vi.fn().mockReturnValue(true), validateCapsWithAppA11y: vi.fn().mockReturnValue(true), + validateCapsWithNonBstackA11y: vi.fn().mockReturnValue(true), shouldScanTestForAccessibility: vi.fn().mockReturnValue(true), getAppA11yResults: vi.fn().mockResolvedValue([]), getAppA11yResultsSummary: vi.fn().mockResolvedValue({}), _getParamsForAppAccessibility: vi.fn().mockReturnValue('{}'), - formatString: vi.fn().mockReturnValue('formatted-script') + formatString: vi.fn().mockReturnValue('formatted-script'), + isBrowserstackSession: vi.fn().mockReturnValue(true) })) vi.mock('../../../src/cli/grpcClient.js', () => ({ @@ -49,12 +65,26 @@ vi.mock('../../../src/cli/grpcClient.js', () => ({ } })) +vi.mock('../../../src/cli/index.js', () => ({ + BrowserstackCLI: { + getInstance: vi.fn().mockReturnValue({ + options: { + accessibilityOptions: { + autoScanning: true + } + } + }) + } +})) + import AccessibilityModule from '../../../src/cli/modules/accessibilityModule.js' import TestFramework from '../../../src/cli/frameworks/testFramework.js' import AutomationFramework from '../../../src/cli/frameworks/automationFramework.js' import { AutomationFrameworkState } from '../../../src/cli/states/automationFrameworkState.js' import { HookState } from '../../../src/cli/states/hookState.js' import { TestFrameworkState } from '../../../src/cli/states/testFrameworkState.js' +import { BrowserstackCLI } from '../../../src/cli/index.js' +import { shouldScanTestForAccessibility, validateCapsWithA11y } from '../../../src/util.js' describe('AccessibilityModule', () => { let accessibilityModule: AccessibilityModule @@ -66,6 +96,14 @@ describe('AccessibilityModule', () => { beforeEach(() => { vi.clearAllMocks() + vi.mocked(BrowserstackCLI.getInstance).mockReturnValue({ + options: { + accessibilityOptions: { + autoScanning: true + } + } + } as any) + mockAccessibilityConfig = { isAppAccessibility: false } @@ -89,16 +127,24 @@ describe('AccessibilityModule', () => { vi.mocked(AutomationFramework.getTrackedInstance).mockReturnValue(mockAutoInstance) vi.mocked(AutomationFramework.getDriver).mockReturnValue(mockBrowser) vi.mocked(AutomationFramework.getState).mockImplementation((instance, key) => { - if (key.includes('SESSION_ID')) { + if (key === 'framework_session_id') { return 12345 } + if (key.includes('IS_BROWSERSTACK_HUB')) { + return true + } + if (key.includes('CAPABILITIES')) { + return { browserName: 'chrome' } + } + if (key.includes('INPUT_CAPABILITIES')) { + return {} + } return {} }) vi.mocked(TestFramework.getTrackedInstance).mockReturnValue(mockTestInstance) - accessibilityModule = new AccessibilityModule(mockAccessibilityConfig) - + accessibilityModule = new AccessibilityModule(mockAccessibilityConfig, false) accessibilityModule.config = { accessibilityOptions: {} } }) @@ -129,42 +175,28 @@ describe('AccessibilityModule', () => { expect(accessibilityModule.name).toBe('AccessibilityModule') expect(accessibilityModule.accessibility).toBe(true) expect(accessibilityModule.isAppAccessibility).toBe(false) + expect(accessibilityModule.isNonBstackA11y).toBe(false) expect(accessibilityModule.accessibilityConfig).toBe(mockAccessibilityConfig) expect(accessibilityModule.accessibilityMap).toBeInstanceOf(Map) expect(accessibilityModule.LOG_DISABLED_SHOWN).toBeInstanceOf(Map) }) it('should set isAppAccessibility from config', () => { - const appConfig = { isAppAccessibility: true } - const module = new AccessibilityModule(appConfig) + const appConfig = { isAppAccessibility: true } as any + const module = new AccessibilityModule(appConfig, false) expect(module.isAppAccessibility).toBe(true) }) }) - describe('getModuleName', () => { - it('should return the correct module name', () => { - expect(accessibilityModule.getModuleName()).toBe('BaseModule') // AccessibilityModule doesn't override getModuleName - expect(AccessibilityModule.MODULE_NAME).toBe('AccessibilityModule') - }) - }) - describe('onBeforeExecute', () => { it('should patch browser methods when automation instance exists', async () => { - vi.mocked(AutomationFramework.getState).mockImplementation((instance, key) => { - if (key.includes('CAPABILITIES')) { - return { browserName: 'chrome' } - } - if (key.includes('INPUT_CAPABILITIES')) { - return {} - } - return 12345 - }) - await accessibilityModule.onBeforeExecute() expect(mockBrowser.getAccessibilityResultsSummary).toBeDefined() expect(mockBrowser.getAccessibilityResults).toBeDefined() expect(mockBrowser.performScan).toBeDefined() + expect(mockBrowser.startA11yScanning).toBeDefined() + expect(mockBrowser.stopA11yScanning).toBeDefined() }) it('should return early when no automation instance found', async () => { @@ -184,9 +216,55 @@ describe('AccessibilityModule', () => { expect(mockBrowser.getAccessibilityResultsSummary).toBeUndefined() }) + + it('should show warning when startA11yScanning is called outside test', async () => { + const loggerWarnSpy = vi.spyOn(accessibilityModule.logger, 'warn') + // Enable accessibility for this test + accessibilityModule.accessibility = true + // Mock validation to return true so accessibility stays enabled + vi.mocked(validateCapsWithA11y).mockReturnValue(true) + + await accessibilityModule.onBeforeExecute() + + await mockBrowser.startA11yScanning() + + expect(loggerWarnSpy).toHaveBeenCalledWith('Accessibility scanning cannot be started from outside the test') + }) + + it('should show warning when stopA11yScanning is called outside test', async () => { + const loggerWarnSpy = vi.spyOn(accessibilityModule.logger, 'warn') + // Enable accessibility for this test + accessibilityModule.accessibility = true + // Mock validation to return true so accessibility stays enabled + vi.mocked(validateCapsWithA11y).mockReturnValue(true) + + await accessibilityModule.onBeforeExecute() + + await mockBrowser.stopA11yScanning() + + expect(loggerWarnSpy).toHaveBeenCalledWith('Accessibility scanning cannot be stopped from outside the test') + }) }) describe('onBeforeTest', () => { + beforeEach(() => { + // Mock shouldScanTestForAccessibility to return true for proper test behavior + vi.mocked(shouldScanTestForAccessibility).mockReturnValue(true) + }) + + it('should patch browser methods when automation instance exists', async () => { + const mockArgs = { + suiteTitle: 'Test Suite', + test: { title: 'Test Case' } + } + + await accessibilityModule.onBeforeTest(mockArgs) + + expect(mockBrowser.performScan).toBeDefined() + expect(mockBrowser.startA11yScanning).toBeDefined() + expect(mockBrowser.stopA11yScanning).toBeDefined() + }) + it('should set up accessibility metadata for test', async () => { const mockArgs = { suiteTitle: 'Test Suite', @@ -203,6 +281,26 @@ describe('AccessibilityModule', () => { expect(TestFramework.setState).toHaveBeenCalled() }) + + it('should enable accessibility scanning for test when conditions are met', async () => { + // Enable accessibility and auto scanning for this test + accessibilityModule.accessibility = true + accessibilityModule.autoScanning = true + // Set up config property that's expected by onBeforeTest + accessibilityModule.config = { accessibilityOptions: {} } + + const mockArgs = { + suiteTitle: 'Test Suite', + test: { title: 'Test Case' } + } + + // Ensure the mock returns true + vi.mocked(shouldScanTestForAccessibility).mockReturnValue(true) + + await accessibilityModule.onBeforeTest(mockArgs) + + expect(accessibilityModule.accessibilityMap.get(12345)).toBe(true) + }) }) describe('onAfterTest', () => { @@ -224,6 +322,24 @@ describe('AccessibilityModule', () => { expect(mockBrowser.executeAsync).not.toHaveBeenCalled() }) + + it('should process accessibility results when scan was started', async () => { + const loggerInfoSpy = vi.spyOn(accessibilityModule.logger, 'info') + + // Mock the getDriverExecuteParams method to avoid async issues + vi.spyOn(accessibilityModule as any, 'getDriverExecuteParams').mockResolvedValue({}) + vi.spyOn(accessibilityModule as any, 'sendTestStopEvent').mockResolvedValue(undefined) + + vi.mocked(mockTestInstance.getData).mockReturnValue({ + accessibilityScanStarted: true, + scanTestForAccessibility: true + }) + + await accessibilityModule.onAfterTest() + + expect(loggerInfoSpy).toHaveBeenCalledWith('Automate test case execution has ended. Processing for accessibility testing is underway.') + expect(loggerInfoSpy).toHaveBeenCalledWith('Accessibility testing for this test case has ended.') + }) }) describe('performScanCli', () => { @@ -285,7 +401,7 @@ describe('AccessibilityModule', () => { }) describe('getA11yResults', () => { - it('should return empty array when accessibility is disabled', async () => { + it('should return undefined when accessibility is disabled', async () => { accessibilityModule.accessibility = false const result = await accessibilityModule.getA11yResults(mockBrowser) @@ -355,4 +471,4 @@ describe('AccessibilityModule', () => { expect(result).toEqual({}) }) }) -}) +}) \ No newline at end of file From 6621d645edf2917b945b6f9df9c2de98be908dec Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 18:35:13 +0530 Subject: [PATCH 08/22] chore: added UTs --- .../cli/modules/accessibilityModule.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts index 60c4608f92e..ced65d860b0 100644 --- a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -188,8 +188,25 @@ describe('AccessibilityModule', () => { }) }) + describe('getModuleName', () => { + it('should return the correct module name', () => { + expect(accessibilityModule.getModuleName()).toBe('BaseModule') // AccessibilityModule doesn't override getModuleName + expect(AccessibilityModule.MODULE_NAME).toBe('AccessibilityModule') + }) + }) + describe('onBeforeExecute', () => { it('should patch browser methods when automation instance exists', async () => { + vi.mocked(AutomationFramework.getState).mockImplementation((instance, key) => { + if (key.includes('CAPABILITIES')) { + return { browserName: 'chrome' } + } + if (key.includes('INPUT_CAPABILITIES')) { + return {} + } + return 12345 + }) + await accessibilityModule.onBeforeExecute() expect(mockBrowser.getAccessibilityResultsSummary).toBeDefined() From 06d2ae2ac5351e4254ebb601a29b8862a42e29a6 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 18:35:25 +0530 Subject: [PATCH 09/22] chore: added UTs --- .../tests/cli/modules/accessibilityModule.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts index ced65d860b0..3a3f982a710 100644 --- a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -197,7 +197,7 @@ describe('AccessibilityModule', () => { describe('onBeforeExecute', () => { it('should patch browser methods when automation instance exists', async () => { - vi.mocked(AutomationFramework.getState).mockImplementation((instance, key) => { + vi.mocked(AutomationFramework.getState).mockImplementation((instance, key) => { if (key.includes('CAPABILITIES')) { return { browserName: 'chrome' } } From ec04883e2c011505e75455b9feba0f7a505790b9 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 19:20:56 +0530 Subject: [PATCH 10/22] minor change --- .../src/cli/modules/accessibilityModule.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 35785df4484..c9c3e334b86 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -108,21 +108,21 @@ export default class AccessibilityModule extends BaseModule { //patching performScan (browser as any).performScan = async () => { - if (!this.accessibility){ + if (!this.accessibility || !this.isAppAccessibility){ return } return await this.performScanCli(browser) } (browser as any).startA11yScanning = async () => { - if (!this.accessibility){ + if (!this.accessibility || !this.isAppAccessibility){ return } this.logger.warn('Accessibility scanning cannot be started from outside the test') } (browser as any).stopA11yScanning = async () => { - if (!this.accessibility){ + if (!this.accessibility || !this.isAppAccessibility){ return } this.logger.warn('Accessibility scanning cannot be stopped from outside the test') @@ -214,7 +214,7 @@ export default class AccessibilityModule extends BaseModule { const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser (browser as any).startA11yScanning = async () => { - if (!this.accessibility){ + if (!this.accessibility || !this.isAppAccessibility){ return } this.accessibilityMap.set(sessionId, true) @@ -227,7 +227,7 @@ export default class AccessibilityModule extends BaseModule { } (browser as any).stopA11yScanning = async () => { - if (!this.accessibility){ + if (!this.accessibility || !this.isAppAccessibility){ return } this.accessibilityMap.set(sessionId, false) @@ -235,7 +235,7 @@ export default class AccessibilityModule extends BaseModule { } (browser as any).performScan = async () => { - if (!this.accessibility){ + if (!this.accessibility || !this.isAppAccessibility){ return } const results = await this.performScanCli(browser) From 3e77594cf32de5b4db5cd217ca819ad40d2ba156 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 19:42:17 +0530 Subject: [PATCH 11/22] minor change --- .../tests/cli/modules/accessibilityModule.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts index 3a3f982a710..18ff27ae405 100644 --- a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -238,6 +238,7 @@ describe('AccessibilityModule', () => { const loggerWarnSpy = vi.spyOn(accessibilityModule.logger, 'warn') // Enable accessibility for this test accessibilityModule.accessibility = true + accessibilityModule.isAppAccessibility = true // Mock validation to return true so accessibility stays enabled vi.mocked(validateCapsWithA11y).mockReturnValue(true) @@ -252,6 +253,7 @@ describe('AccessibilityModule', () => { const loggerWarnSpy = vi.spyOn(accessibilityModule.logger, 'warn') // Enable accessibility for this test accessibilityModule.accessibility = true + accessibilityModule.isAppAccessibility = true // Mock validation to return true so accessibility stays enabled vi.mocked(validateCapsWithA11y).mockReturnValue(true) From 6cdd11ca86423460b7d2cd0d882bbe7429cdd867 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 19:56:26 +0530 Subject: [PATCH 12/22] minor change --- .../tests/cli/modules/accessibilityModule.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts index 18ff27ae405..b24a8940a70 100644 --- a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -84,7 +84,7 @@ import { AutomationFrameworkState } from '../../../src/cli/states/automationFram import { HookState } from '../../../src/cli/states/hookState.js' import { TestFrameworkState } from '../../../src/cli/states/testFrameworkState.js' import { BrowserstackCLI } from '../../../src/cli/index.js' -import { shouldScanTestForAccessibility, validateCapsWithA11y } from '../../../src/util.js' +import { shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y } from '../../../src/util.js' describe('AccessibilityModule', () => { let accessibilityModule: AccessibilityModule @@ -241,7 +241,8 @@ describe('AccessibilityModule', () => { accessibilityModule.isAppAccessibility = true // Mock validation to return true so accessibility stays enabled vi.mocked(validateCapsWithA11y).mockReturnValue(true) - + vi.mocked(validateCapsWithAppA11y).mockReturnValue(true) + await accessibilityModule.onBeforeExecute() await mockBrowser.startA11yScanning() @@ -256,6 +257,7 @@ describe('AccessibilityModule', () => { accessibilityModule.isAppAccessibility = true // Mock validation to return true so accessibility stays enabled vi.mocked(validateCapsWithA11y).mockReturnValue(true) + vi.mocked(validateCapsWithAppA11y).mockReturnValue(true) await accessibilityModule.onBeforeExecute() From 3e780f0ffc6a25d5c346bac2740878eb6a906df6 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Tue, 16 Sep 2025 20:04:07 +0530 Subject: [PATCH 13/22] fix: lint error --- .../tests/cli/modules/accessibilityModule.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts index b24a8940a70..e0e36768d4d 100644 --- a/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/wdio-browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -242,7 +242,7 @@ describe('AccessibilityModule', () => { // Mock validation to return true so accessibility stays enabled vi.mocked(validateCapsWithA11y).mockReturnValue(true) vi.mocked(validateCapsWithAppA11y).mockReturnValue(true) - + await accessibilityModule.onBeforeExecute() await mockBrowser.startA11yScanning() From 8f12fde6d48d7d518f93b4f4bbc9c7f6b50f6917 Mon Sep 17 00:00:00 2001 From: Rahul Dandona <87532892+dandonarahul2002@users.noreply.github.com> Date: Wed, 17 Sep 2025 07:45:29 +0530 Subject: [PATCH 14/22] Jasmine session name marking bstack (#14768) * fix: session name marking for browserstack-service via jasmine * fix: use sessionName from userCaps for Jasmine * chore: remove semicolon --- packages/wdio-browserstack-service/src/service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/wdio-browserstack-service/src/service.ts b/packages/wdio-browserstack-service/src/service.ts index b842dc073bf..d536b572589 100644 --- a/packages/wdio-browserstack-service/src/service.ts +++ b/packages/wdio-browserstack-service/src/service.ts @@ -715,11 +715,14 @@ export default class BrowserstackService implements Services.ServiceInstance { suiteTitle, test?.title ) - } else if (test && !test.fullName) { + } else if (test && !test.fullName && this._config.framework !== 'jasmine') { // Mocha const pre = this._options.sessionNamePrependTopLevelSuiteTitle ? `${suiteTitle} - ` : '' const post = !this._options.sessionNameOmitTestTitle ? ` - ${test.title}` : '' name = `${pre}${test.parent}${post}` + } else if (this._config.framework === 'jasmine') { + // Jasmine + name = (this._caps as any)?.['bstack:options']?.sessionName } if (!BrowserstackCLI.getInstance().isRunning()) { From e2975a8f69abc26353192ba1cb3376f437fab226 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Wed, 17 Sep 2025 19:53:10 +0530 Subject: [PATCH 15/22] review changes --- .../src/cli/modules/accessibilityModule.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index c9c3e334b86..ba2e663bac7 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -108,21 +108,21 @@ export default class AccessibilityModule extends BaseModule { //patching performScan (browser as any).performScan = async () => { - if (!this.accessibility || !this.isAppAccessibility){ + if (!this.accessibility && !this.isAppAccessibility){ return } return await this.performScanCli(browser) } (browser as any).startA11yScanning = async () => { - if (!this.accessibility || !this.isAppAccessibility){ + if (!this.accessibility && !this.isAppAccessibility){ return } this.logger.warn('Accessibility scanning cannot be started from outside the test') } (browser as any).stopA11yScanning = async () => { - if (!this.accessibility || !this.isAppAccessibility){ + if (!this.accessibility && !this.isAppAccessibility){ return } this.logger.warn('Accessibility scanning cannot be stopped from outside the test') From 73d1b2cb36cf069207d47c544e7e1ea108221b6a Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Wed, 17 Sep 2025 20:05:34 +0530 Subject: [PATCH 16/22] review changes --- .../src/cli/modules/accessibilityModule.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index ba2e663bac7..51943873a35 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -214,7 +214,7 @@ export default class AccessibilityModule extends BaseModule { const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser (browser as any).startA11yScanning = async () => { - if (!this.accessibility || !this.isAppAccessibility){ + if (!this.accessibility && !this.isAppAccessibility){ return } this.accessibilityMap.set(sessionId, true) @@ -227,7 +227,7 @@ export default class AccessibilityModule extends BaseModule { } (browser as any).stopA11yScanning = async () => { - if (!this.accessibility || !this.isAppAccessibility){ + if (!this.accessibility && !this.isAppAccessibility){ return } this.accessibilityMap.set(sessionId, false) @@ -235,7 +235,7 @@ export default class AccessibilityModule extends BaseModule { } (browser as any).performScan = async () => { - if (!this.accessibility || !this.isAppAccessibility){ + if (!this.accessibility && !this.isAppAccessibility){ return } const results = await this.performScanCli(browser) From 2a120f43fba4f12cbdef9f57c0d17c0268979977 Mon Sep 17 00:00:00 2001 From: Bhargavi-BS Date: Wed, 17 Sep 2025 21:01:11 +0530 Subject: [PATCH 17/22] minor change --- .../src/cli/modules/accessibilityModule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 51943873a35..1ad9de061a2 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -407,7 +407,7 @@ export default class AccessibilityModule extends BaseModule { } if (sessionId && this.accessibilityMap.get(sessionId)) { this.logger.debug('Performing scan before saving results') - await this.performScanCli(browser, 'afterTest') + await this.performScanCli(browser) } if (this.isAppAccessibility) { return From a91071b5665673f9907485b26127d0750c5f5895 Mon Sep 17 00:00:00 2001 From: Pritish Chugh <54476309+Pritishchugh22@users.noreply.github.com> Date: Thu, 25 Sep 2025 14:52:03 +0530 Subject: [PATCH 18/22] Moving _setAnnotation code into utils --- .../src/accessibility-handler.ts | 13 +++-------- .../src/cli/modules/accessibilityModule.ts | 12 ++-------- .../wdio-browserstack-service/src/util.ts | 22 +++++++++++++++++++ 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/wdio-browserstack-service/src/accessibility-handler.ts b/packages/wdio-browserstack-service/src/accessibility-handler.ts index c1aead50850..f234dad9112 100644 --- a/packages/wdio-browserstack-service/src/accessibility-handler.ts +++ b/packages/wdio-browserstack-service/src/accessibility-handler.ts @@ -67,7 +67,8 @@ import { isTrue, validateCapsWithAppA11y, getAppA11yResults, - isFalse + isFalse, + setBrowserstackAnnotation } from './util.js' import accessibilityScripts from './scripts/accessibility-scripts.js' import PerformanceTester from './instrumentation/performance/performance-tester.js' @@ -470,15 +471,7 @@ class _AccessibilityHandler { } private async _setAnnotation(message: string) { - if (this._accessibility && isBrowserstackSession(this._browser)) { - await (this._browser as WebdriverIO.Browser).execute(`browserstack_executor: ${JSON.stringify({ - action: 'annotate', - arguments: { - data: message, - level: 'info' - } - })}`) - } + await setBrowserstackAnnotation(this._browser as WebdriverIO.Browser, message, Boolean(this._accessibility)) } } diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 1ad9de061a2..c7233f24464 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -10,7 +10,7 @@ import { TestFrameworkState } from '../states/testFrameworkState.js' import { AutomationFrameworkState } from '../states/automationFrameworkState.js' import { HookState } from '../states/hookState.js' import accessibilityScripts from '../../scripts/accessibility-scripts.js' -import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, validateCapsWithNonBstackA11y, isBrowserstackSession } from '../../util.js' +import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, validateCapsWithNonBstackA11y, isBrowserstackSession, setBrowserstackAnnotation } from '../../util.js' import { AutomationFrameworkConstants } from '../frameworks/constants/automationFrameworkConstants.js' import util from 'node:util' import type { Accessibility } from '@browserstack/wdio-browserstack-service' @@ -482,15 +482,7 @@ export default class AccessibilityModule extends BaseModule { const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() const browser = AutomationFramework.getDriver(autoInstance) as WebdriverIO.Browser - if (this.accessibility && isBrowserstackSession(browser)) { - await (browser as WebdriverIO.Browser).execute(`browserstack_executor: ${JSON.stringify({ - action: 'annotate', - arguments: { - data: message, - level: 'info' - } - })}`) - } + await setBrowserstackAnnotation(browser, message, this.accessibility) } } diff --git a/packages/wdio-browserstack-service/src/util.ts b/packages/wdio-browserstack-service/src/util.ts index 0003c310f22..17481a9b990 100644 --- a/packages/wdio-browserstack-service/src/util.ts +++ b/packages/wdio-browserstack-service/src/util.ts @@ -1034,6 +1034,28 @@ export function isBrowserstackSession(browser?: WebdriverIO.Browser | WebdriverI return browser && getCloudProvider(browser).toLowerCase() === 'browserstack' } +/** + * Sets a BrowserStack annotation with the provided message + * @param browser - The WebDriver browser instance + * @param message - The annotation message to set + * @param isAccessibilityEnabled - Whether accessibility is enabled for the session + */ +export async function setBrowserstackAnnotation( + browser: WebdriverIO.Browser, + message: string, + isAccessibilityEnabled: boolean +): Promise { + if (isAccessibilityEnabled && isBrowserstackSession(browser)) { + await browser.execute(`browserstack_executor: ${JSON.stringify({ + action: 'annotate', + arguments: { + data: message, + level: 'info' + } + })}`) + } +} + export function getScenarioExamples(world: ITestCaseHookParameter) { const scenario = world.pickle From e6cf2df2ed6ff2fbc93de3ebb641fea29b4a752d Mon Sep 17 00:00:00 2001 From: Pritish Chugh <54476309+Pritishchugh22@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:46:15 +0530 Subject: [PATCH 19/22] removing redundant vars --- .../src/cli/modules/accessibilityModule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index c7233f24464..a2c2baa8356 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -10,7 +10,7 @@ import { TestFrameworkState } from '../states/testFrameworkState.js' import { AutomationFrameworkState } from '../states/automationFrameworkState.js' import { HookState } from '../states/hookState.js' import accessibilityScripts from '../../scripts/accessibility-scripts.js' -import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, validateCapsWithNonBstackA11y, isBrowserstackSession, setBrowserstackAnnotation } from '../../util.js' +import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, validateCapsWithNonBstackA11y, setBrowserstackAnnotation } from '../../util.js' import { AutomationFrameworkConstants } from '../frameworks/constants/automationFrameworkConstants.js' import util from 'node:util' import type { Accessibility } from '@browserstack/wdio-browserstack-service' From f2377bbec65cc9cf1cb1ed12ef4eb737ce4d7117 Mon Sep 17 00:00:00 2001 From: WebdriverIO Release Bot Date: Wed, 29 Oct 2025 15:51:35 +0000 Subject: [PATCH 20/22] v8.47.1 --- lerna.json | 2 +- package-lock.json | 2 +- packages/wdio-browserstack-service/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lerna.json b/lerna.json index f5dab0dfec2..bb4849bc88d 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages": [ "packages/*" ], - "version": "8.47.0", + "version": "8.47.1", "command": { "bootstrap": { "npmClientArgs": [ diff --git a/package-lock.json b/package-lock.json index 7480b56051b..83e4c395ec2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31597,7 +31597,7 @@ }, "packages/wdio-browserstack-service": { "name": "@wdio/browserstack-service", - "version": "8.47.0", + "version": "8.47.1", "license": "MIT", "dependencies": { "@browserstack/ai-sdk-node": "1.5.17", diff --git a/packages/wdio-browserstack-service/package.json b/packages/wdio-browserstack-service/package.json index 7fe272edc7b..368b6b3b3d3 100644 --- a/packages/wdio-browserstack-service/package.json +++ b/packages/wdio-browserstack-service/package.json @@ -1,6 +1,6 @@ { "name": "@wdio/browserstack-service", - "version": "8.47.0", + "version": "8.47.1", "description": "WebdriverIO service for better Browserstack integration", "author": "Adam Bjerstedt ", "homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-browserstack-service", From 2dbc7b728fb8263f020965acc7a4931032677186 Mon Sep 17 00:00:00 2001 From: Priyanka Gadhiya <110826968+pri-gadhiya@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:45:46 +0530 Subject: [PATCH 21/22] Adding extra key for app accessibility feature to use centralHeader for authentication (#14844) * SDK-4415 Added changes for central auth flow v2 * SDK-4415 * SDK-4415 Added change for adding header --- .../src/cli/cliUtils.ts | 3 +- .../src/cli/modules/accessibilityModule.ts | 44 +++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/wdio-browserstack-service/src/cli/cliUtils.ts b/packages/wdio-browserstack-service/src/cli/cliUtils.ts index 16c34ae7a94..ee6bf2e2c88 100644 --- a/packages/wdio-browserstack-service/src/cli/cliUtils.ts +++ b/packages/wdio-browserstack-service/src/cli/cliUtils.ts @@ -174,7 +174,8 @@ export class CLIUtils { sdk_language: this.getSdkLanguage(), } if (!isNullOrEmpty(existingCliPath)) { - queryParams.cli_version = await this.runShellCommand(`${existingCliPath} version`) + const nullDevice = platform() === 'win32' ? 'NUL' : '/dev/null' + queryParams.cli_version = await this.runShellCommand(`${existingCliPath} version 2>${nullDevice}`) } const response = await this.requestToUpdateCLI(queryParams, config) if (nestedKeyValue(response, ['updated_cli_version'])) { diff --git a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts index 754aed7c7db..bbe5f5a42e8 100644 --- a/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/wdio-browserstack-service/src/cli/modules/accessibilityModule.ts @@ -31,6 +31,8 @@ export default class AccessibilityModule extends BaseModule { static MODULE_NAME = 'AccessibilityModule' accessibilityMap: Map LOG_DISABLED_SHOWN: Map + centralAuthA11yConfig: Record = {} + centralAuthConfigFetched: boolean = false constructor(accessibilityConfig: Accessibility, isNonBstackA11y: boolean) { super() @@ -245,7 +247,7 @@ export default class AccessibilityModule extends BaseModule { 'thBuildUuid': process.env.BROWSERSTACK_TESTHUB_UUID, 'thJwtToken': process.env.BROWSERSTACK_TESTHUB_JWT } - const driverExecuteParams = await this.getDriverExecuteParams() + const driverExecuteParams = await this.getDriverExecuteParams('saveResults') dataForExtension = { ...dataForExtension, ...driverExecuteParams } // final scan and saving the results @@ -307,13 +309,26 @@ export default class AccessibilityModule extends BaseModule { PERFORMANCE_SDK_EVENTS.A11Y_EVENTS.PERFORM_SCAN, async () => { try { + // Fetch central auth accessibility configuration + const centralAuthConfig = await this.fetchCentralAuthA11yConfig('scan') if (!this.accessibility) { this.logger.debug('Not an Accessibility Automation session.') return } if (this.isAppAccessibility) { + // Get app accessibility params and merge with central auth config + const appAccessibilityParams = _getParamsForAppAccessibility(commandName) + + // Merge with central auth config + const mergedParams: Record = { ...appAccessibilityParams, ...centralAuthConfig } + + // Use centralAuthToken if available + if (centralAuthConfig.centralAuthToken) { + // Set the auth header with the token value + mergedParams.centralAuthHeader = centralAuthConfig.centralAuthToken + } const results: unknown = await (browser as WebdriverIO.Browser).execute( - formatString(this.scriptInstance.performScan, JSON.stringify(_getParamsForAppAccessibility(commandName))) as string, + formatString(this.scriptInstance.performScan, JSON.stringify(mergedParams)) as string, {} ) BStackLogger.debug(util.format(results as string)) @@ -399,10 +414,10 @@ export default class AccessibilityModule extends BaseModule { )() } - async getDriverExecuteParams() { + async getDriverExecuteParams(scriptName: string) { const payload: Omit = { product: 'accessibility', - scriptName: 'saveResults' + scriptName: scriptName } const response: FetchDriverExecuteParamsEventResponse = await GrpcClient.getInstance().fetchDriverExecuteParamsEvent(payload) if (response.success) { @@ -412,4 +427,25 @@ export default class AccessibilityModule extends BaseModule { return {} } + /** + * Fetch central auth accessibility configuration for the given script name. + * Returns cached config if already fetched, otherwise loads and caches it. + * + * @param scriptName - Name of the script to fetch config for + * @returns Configuration object, empty object if error occurs + */ + async fetchCentralAuthA11yConfig(scriptName: string): Promise> { + try { + if (this.centralAuthConfigFetched) { + return this.centralAuthA11yConfig + } + this.centralAuthA11yConfig = await this.getDriverExecuteParams(scriptName) + this.centralAuthConfigFetched = true + return this.centralAuthA11yConfig + } catch (error) { + this.logger.error(`fetchCentralAuthA11yConfig: Failed to fetch driver execute params for ${scriptName}: ${util.format(error)}`) + return {} + } + } + } From 9a4aeb53bad8f86e8caa232f67a5b51fcde1fdf2 Mon Sep 17 00:00:00 2001 From: WebdriverIO Release Bot Date: Sat, 29 Nov 2025 18:34:34 +0000 Subject: [PATCH 22/22] v8.48.0 --- CHANGELOG.md | 10 ++++++++++ lerna.json | 2 +- package-lock.json | 2 +- packages/wdio-browserstack-service/package.json | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 600fa6c9dc9..4dd4c5f6247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,16 @@ See [CHANGELOG - v7](https://github.com/webdriverio/webdriverio/blob/v7/CHANGELO --- +## v8.48.0 (2025-11-29) + +#### :bug: Bug Fix +* `wdio-browserstack-service` + * [#14844](https://github.com/webdriverio/webdriverio/pull/14844) Adding extra key for app accessibility feature to use centralHeader for authentication ([@pri-gadhiya](https://github.com/pri-gadhiya)) + +#### Committers: 1 +- Priyanka Gadhiya ([@pri-gadhiya](https://github.com/pri-gadhiya)) + + ## v8.47.0 (2025-09-09) #### :rocket: New Feature diff --git a/lerna.json b/lerna.json index bb4849bc88d..72f5e234881 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages": [ "packages/*" ], - "version": "8.47.1", + "version": "8.48.0", "command": { "bootstrap": { "npmClientArgs": [ diff --git a/package-lock.json b/package-lock.json index 83e4c395ec2..923ea5a2fe3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31597,7 +31597,7 @@ }, "packages/wdio-browserstack-service": { "name": "@wdio/browserstack-service", - "version": "8.47.1", + "version": "8.48.0", "license": "MIT", "dependencies": { "@browserstack/ai-sdk-node": "1.5.17", diff --git a/packages/wdio-browserstack-service/package.json b/packages/wdio-browserstack-service/package.json index 368b6b3b3d3..5376c7c0953 100644 --- a/packages/wdio-browserstack-service/package.json +++ b/packages/wdio-browserstack-service/package.json @@ -1,6 +1,6 @@ { "name": "@wdio/browserstack-service", - "version": "8.47.1", + "version": "8.48.0", "description": "WebdriverIO service for better Browserstack integration", "author": "Adam Bjerstedt ", "homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-browserstack-service",