diff --git a/packages/wdio-browserstack-service/src/@types/bstack-service-types.d.ts b/packages/wdio-browserstack-service/src/@types/bstack-service-types.d.ts index e139b420130..05b92339bfd 100644 --- a/packages/wdio-browserstack-service/src/@types/bstack-service-types.d.ts +++ b/packages/wdio-browserstack-service/src/@types/bstack-service-types.d.ts @@ -1,11 +1,13 @@ declare namespace WebdriverIO { interface Browser { getAccessibilityResultsSummary: () => Promise<{ [key: string]: any; }>, - getAccessibilityResults: () => Promise> + getAccessibilityResults: () => Promise>, + performScan: () => Promise<{ [key: string]: any; } | undefined> } interface MultiRemoteBrowser { getAccessibilityResultsSummary: () => Promise<{ [key: string]: any; }>, - getAccessibilityResults: () => Promise> + getAccessibilityResults: () => Promise>, + performScan: () => Promise<{ [key: string]: any; } | undefined> } } diff --git a/packages/wdio-browserstack-service/src/accessibility-handler.ts b/packages/wdio-browserstack-service/src/accessibility-handler.ts index a20ee51f966..906c542395b 100644 --- a/packages/wdio-browserstack-service/src/accessibility-handler.ts +++ b/packages/wdio-browserstack-service/src/accessibility-handler.ts @@ -1,3 +1,5 @@ +import util from 'node:util' + import type { Capabilities, Frameworks } from '@wdio/types' import type { ITestCaseHookParameter } from './cucumber-types.js' @@ -5,6 +7,7 @@ import type { ITestCaseHookParameter } from './cucumber-types.js' import { getA11yResultsSummary, getA11yResults, + performA11yScan, getUniqueIdentifier, getUniqueIdentifierForCucumber, isAccessibilityAutomationSession, @@ -14,7 +17,8 @@ import { validateCapsWithA11y, isTrue } from './util.js' -import { testForceStop, testStartEvent, testStop } from './scripts/test-event-scripts.js' +import accessibilityScripts from './scripts/accessibility-scripts.js' + import { BStackLogger } from './bstackLogger.js' class _AccessibilityHandler { @@ -24,6 +28,8 @@ class _AccessibilityHandler { private _accessibility?: boolean private _accessibilityOptions?: { [key: string]: any; } private _testMetadata: { [key: string]: any; } = {} + private static _a11yScanSessionMap: { [key: string]: any; } = {} + private _sessionId: string | null = null constructor ( private _browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser, @@ -80,7 +86,8 @@ class _AccessibilityHandler { } } - async before () { + async before (sessionId: string) { + this._sessionId = sessionId this._accessibility = isTrue(this._getCapabilityValue(this._caps, 'accessibility', 'browserstack.accessibility')) if (isBrowserstackSession(this._browser) && isAccessibilityAutomationSession(this._accessibility)) { @@ -97,31 +104,48 @@ class _AccessibilityHandler { (this._browser as WebdriverIO.Browser).getAccessibilityResults = async () => { return await getA11yResults((this._browser as WebdriverIO.Browser), isBrowserstackSession(this._browser), this._accessibility) } - } - async beforeTest (suiteTitle: string | undefined, test: Frameworks.Test) { - if ( - this._framework !== 'mocha' || - !this.shouldRunTestHooks(this._browser, this._accessibility) - ) { - return + (this._browser as WebdriverIO.Browser).performScan = async () => { + return await performA11yScan((this._browser as WebdriverIO.Browser), isBrowserstackSession(this._browser), this._accessibility) } - const shouldScanTest = shouldScanTestForAccessibility(suiteTitle, test.title, this._accessibilityOptions) - const testIdentifier = this.getIdentifier(test) - const isPageOpened = await this.checkIfPageOpened(this._browser, testIdentifier, shouldScanTest) - - if (!isPageOpened) { + if (!this._accessibility) { + return + } + if (!('overwriteCommand' in this._browser && Array.isArray(accessibilityScripts.commandsToWrap))) { return } + accessibilityScripts.commandsToWrap + .filter((command) => command.name && command.class) + .forEach((command) => { + const browser = this._browser as WebdriverIO.Browser + browser.overwriteCommand(command.name, this.commandWrapper.bind(this, command), command.class === 'Element') + }) + } + + async beforeTest (suiteTitle: string | undefined, test: Frameworks.Test) { try { - if (shouldScanTest) { - BStackLogger.info('Setup for Accessibility testing has started. Automate test case execution will begin momentarily.') - await this.sendTestStartEvent(this._browser as WebdriverIO.Browser) - } else { - await this.sendTestForceStopEvent(this._browser as WebdriverIO.Browser) + if ( + this._framework !== 'mocha' || + !this.shouldRunTestHooks(this._browser, this._accessibility) + ) { + return + } + + const shouldScanTest = shouldScanTestForAccessibility(suiteTitle, test.title, this._accessibilityOptions) + const testIdentifier = this.getIdentifier(test) + const isPageOpened = await this.checkIfPageOpened(this._browser, testIdentifier, shouldScanTest) + + if (this._sessionId) { + /* For case with multiple tests under one browser, before hook of 2nd test should change this map value */ + AccessibilityHandler._a11yScanSessionMap[this._sessionId] = shouldScanTest + } + + if (!isPageOpened) { + return } + this._testMetadata[testIdentifier].accessibilityScanStarted = shouldScanTest if (shouldScanTest) { @@ -179,28 +203,27 @@ class _AccessibilityHandler { * Cucumber Only */ async beforeScenario (world: ITestCaseHookParameter) { - if (!this.shouldRunTestHooks(this._browser, this._accessibility)) { - return - } - const pickleData = world.pickle const gherkinDocument = world.gherkinDocument const featureData = gherkinDocument.feature const uniqueId = getUniqueIdentifierForCucumber(world) - const shouldScanScenario = shouldScanTestForAccessibility(featureData?.name, pickleData.name, this._accessibilityOptions, world, true) - const isPageOpened = await this.checkIfPageOpened(this._browser, uniqueId, shouldScanScenario) - - if (!isPageOpened) { + if (!this.shouldRunTestHooks(this._browser, this._accessibility)) { return } try { - if (shouldScanScenario) { - BStackLogger.info('Setup for Accessibility testing has started. Automate test case execution will begin momentarily.') - await this.sendTestStartEvent(this._browser as WebdriverIO.Browser) - } else { - await this.sendTestForceStopEvent(this._browser as WebdriverIO.Browser) + const shouldScanScenario = shouldScanTestForAccessibility(featureData?.name, pickleData.name, this._accessibilityOptions, world, true) + const isPageOpened = await this.checkIfPageOpened(this._browser, uniqueId, shouldScanScenario) + + if (this._sessionId) { + /* For case with multiple tests under one browser, before hook of 2nd test should change this map value */ + AccessibilityHandler._a11yScanSessionMap[this._sessionId] = shouldScanScenario + } + + if (!isPageOpened) { + return } + this._testMetadata[uniqueId].accessibilityScanStarted = shouldScanScenario if (shouldScanScenario) { @@ -258,16 +281,25 @@ class _AccessibilityHandler { * private methods */ - private sendTestStartEvent(browser: WebdriverIO.Browser) { - return (browser as WebdriverIO.Browser).executeAsync(testStartEvent) - } - - private sendTestForceStopEvent(browser: WebdriverIO.Browser) { - return (browser as WebdriverIO.Browser).execute(testForceStop) + private async commandWrapper (command: any, origFunction: Function, ...args: any[]) { + if ( + this._sessionId && AccessibilityHandler._a11yScanSessionMap[this._sessionId] && + ( + !command.name.includes('execute') || + !AccessibilityHandler.shouldPatchExecuteScript(args.length ? args[0] : null) + ) + ) { + BStackLogger.debug(`Performing scan for ${command.class} ${command.name}`) + await performA11yScan(this._browser, true, true, command.name) + } + return origFunction(...args) } - private sendTestStopEvent(browser: WebdriverIO.Browser, dataForExtension: any) { - return (browser as WebdriverIO.Browser).executeAsync(testStop, dataForExtension) + private async sendTestStopEvent(browser: WebdriverIO.Browser, dataForExtension: any) { + BStackLogger.debug('Performing scan before saving results') + await performA11yScan(browser, true, true) + const results: unknown = await (browser as WebdriverIO.Browser).executeAsync(accessibilityScripts.saveTestResults as string, dataForExtension) + BStackLogger.debug(util.format(results as string)) } private getIdentifier (test: Frameworks.Test | ITestCaseHookParameter) { @@ -301,6 +333,17 @@ class _AccessibilityHandler { return pageOpen } + + private static shouldPatchExecuteScript(script: string | null): Boolean { + if (!script || typeof script !== 'string') { + return true + } + + return ( + script.toLowerCase().indexOf('browserstack_executor') !== -1 || + script.toLowerCase().indexOf('browserstack_accessibility_automation_script') !== -1 + ) + } } // https://github.com/microsoft/TypeScript/issues/6543 @@ -308,4 +351,3 @@ const AccessibilityHandler: typeof _AccessibilityHandler = o11yClassErrorHandler type AccessibilityHandler = _AccessibilityHandler export default AccessibilityHandler - diff --git a/packages/wdio-browserstack-service/src/scripts/accessibility-scripts.ts b/packages/wdio-browserstack-service/src/scripts/accessibility-scripts.ts new file mode 100644 index 00000000000..e3b68a26afc --- /dev/null +++ b/packages/wdio-browserstack-service/src/scripts/accessibility-scripts.ts @@ -0,0 +1,70 @@ +import path from 'node:path' +import fs from 'node:fs' +import os from 'node:os' + +class AccessibilityScripts { + private static instance: AccessibilityScripts | null = null + + public performScan: string | null = null + public getResults: string | null = null + public getResultsSummary: string | null = null + public saveTestResults: string | null = null + public commandsToWrap: Array | null = null + + public browserstackFolderPath = path.join(os.homedir(), '.browserstack') + public commandsPath = path.join(this.browserstackFolderPath, 'commands.json') + + // don't allow to create instances from it other than through `checkAndGetInstance` + private constructor() {} + + public static checkAndGetInstance() { + if (!AccessibilityScripts.instance) { + AccessibilityScripts.instance = new AccessibilityScripts() + AccessibilityScripts.instance.readFromExistingFile() + } + return AccessibilityScripts.instance + } + + public readFromExistingFile() { + try { + if (fs.existsSync(this.commandsPath)) { + const data = fs.readFileSync(this.commandsPath, 'utf8') + if (data) { + this.update(JSON.parse(data)) + } + } + } catch (error: any) { + /* Do nothing */ + } + } + + public update(data: { commands: [any], scripts: { scan: null; getResults: null; getResultsSummary: null; saveResults: null; }; }) { + if (data.scripts) { + this.performScan = data.scripts.scan + this.getResults = data.scripts.getResults + this.getResultsSummary = data.scripts.getResultsSummary + this.saveTestResults = data.scripts.saveResults + } + if (data.commands && data.commands.length) { + this.commandsToWrap = data.commands + } + } + + public store() { + if (!fs.existsSync(this.browserstackFolderPath)){ + fs.mkdirSync(this.browserstackFolderPath) + } + + fs.writeFileSync(this.commandsPath, JSON.stringify({ + commands: this.commandsToWrap, + scripts: { + scan: this.performScan, + getResults: this.getResults, + getResultsSummary: this.getResultsSummary, + saveResults: this.saveTestResults, + } + })) + } +} + +export default AccessibilityScripts.checkAndGetInstance() diff --git a/packages/wdio-browserstack-service/src/scripts/test-event-scripts.ts b/packages/wdio-browserstack-service/src/scripts/test-event-scripts.ts deleted file mode 100644 index 3baee1fb609..00000000000 --- a/packages/wdio-browserstack-service/src/scripts/test-event-scripts.ts +++ /dev/null @@ -1,68 +0,0 @@ -export function testStartEvent() { - const callback = arguments[arguments.length - 1] - const fn = () => { - window.addEventListener('A11Y_TAP_STARTED', fn2) - const e = new CustomEvent('A11Y_FORCE_START') - window.dispatchEvent(e) - } - const fn2 = () => { - window.removeEventListener('A11Y_TAP_STARTED', fn) - callback() - } - fn() -} - -export function testForceStop() { - const e = new CustomEvent('A11Y_FORCE_STOP') - window.dispatchEvent(e) -} - -export function testStop(this: any) { - const callback = arguments[arguments.length - 1] - - this.res = null - if (arguments[0].saveResults) { - window.addEventListener('A11Y_TAP_TRANSPORTER', (event: any) => { - (window as any).tapTransporterData = event.detail - this.res = (window as any).tapTransporterData - callback(this.res) - }) - } - const e = new CustomEvent('A11Y_TEST_END', { detail: arguments[0] }) - window.dispatchEvent(e) - if (arguments[0].saveResults !== true ) { - callback() - } -} - -export function accessibilityResults() : Promise> { - return new Promise(function (resolve, reject) { - try { - const event = new CustomEvent('A11Y_TAP_GET_RESULTS') - const fn = function (event: any) { - window.removeEventListener('A11Y_RESULTS_RESPONSE', fn) - resolve(event.detail.data) - } - window.addEventListener('A11Y_RESULTS_RESPONSE', fn) - window.dispatchEvent(event) - } catch { - reject() - } - }) -} - -export function accessibilityResultsSummary() : Promise<{ [key: string]: any; }> { - return new Promise(function (resolve, reject) { - try { - const event = new CustomEvent('A11Y_TAP_GET_RESULTS_SUMMARY') - const fn = function (event: any) { - window.removeEventListener('A11Y_RESULTS_SUMMARY_RESPONSE', fn) - resolve(event.detail.summary) - } - window.addEventListener('A11Y_RESULTS_SUMMARY_RESPONSE', fn) - window.dispatchEvent(event) - } catch { - reject() - } - }) -} diff --git a/packages/wdio-browserstack-service/src/service.ts b/packages/wdio-browserstack-service/src/service.ts index 40ca8810a04..31c2c07218e 100644 --- a/packages/wdio-browserstack-service/src/service.ts +++ b/packages/wdio-browserstack-service/src/service.ts @@ -147,6 +147,22 @@ export default class BrowserstackService implements Services.ServiceInstance { await this._insightsHandler.before() } + if (isBrowserstackSession(this._browser)) { + try { + this._accessibilityHandler = new AccessibilityHandler( + this._browser, + this._caps, + this._isAppAutomate(), + this._config.framework, + this._accessibility, + this._options.accessibilityOptions + ) + await this._accessibilityHandler.before(sessionId) + } catch (err) { + BStackLogger.error(`[Accessibility Test Run] Error in service class before function: ${err}`) + } + } + /** * register command event */ @@ -186,22 +202,6 @@ export default class BrowserstackService implements Services.ServiceInstance { } } - if (this._browser && isBrowserstackSession(this._browser)) { - try { - this._accessibilityHandler = new AccessibilityHandler( - this._browser, - this._caps, - this._isAppAutomate(), - this._config.framework, - this._accessibility, - this._options.accessibilityOptions - ) - await this._accessibilityHandler.before() - } catch (err) { - BStackLogger.error(`[Accessibility Test Run] Error in service class before function: ${err}`) - } - } - return await this._printSessionURL() } diff --git a/packages/wdio-browserstack-service/src/util.ts b/packages/wdio-browserstack-service/src/util.ts index 404edb4b1e4..ab5b4178c20 100644 --- a/packages/wdio-browserstack-service/src/util.ts +++ b/packages/wdio-browserstack-service/src/util.ts @@ -37,9 +37,9 @@ import { TESTOPS_JWT_ENV } from './constants.js' import CrashReporter from './crash-reporter.js' -import { accessibilityResults, accessibilityResultsSummary } from './scripts/test-event-scripts.js' import { BStackLogger } from './bstackLogger.js' import { FileStream } from './fileStream.js' +import AccessibilityScripts from './scripts/accessibility-scripts.js' import UsageStats from './testOps/usageStats.js' import TestOpsConfig from './testOps/testOpsConfig.js' @@ -410,7 +410,10 @@ export const createAccessibilityTestRun = errorHandler(async function createAcce 'source': { frameworkName: 'WebdriverIO-' + config.framework, frameworkVersion: bsConfig.bstackServiceVersion, - sdkVersion: bsConfig.bstackServiceVersion + sdkVersion: bsConfig.bstackServiceVersion, + language: 'ECMAScript', + testFramework: 'webdriverIO', + testFrameworkVersion: bsConfig.bstackServiceVersion }, 'settings': bsConfig.accessibilityOptions || {}, 'versionControl': await getGitMetaData(), @@ -433,7 +436,7 @@ export const createAccessibilityTestRun = errorHandler(async function createAcce try { const response: any = await nodeRequest( - 'POST', 'test_runs', requestOptions, ACCESSIBILITY_API_URL + 'POST', 'v2/test_runs', requestOptions, ACCESSIBILITY_API_URL ) BStackLogger.debug(`[Create Accessibility Test Run] Success response: ${JSON.stringify(response)}`) @@ -444,9 +447,13 @@ export const createAccessibilityTestRun = errorHandler(async function createAcce if (response.data.id) { process.env.BS_A11Y_TEST_RUN_ID = response.data.id } - BStackLogger.debug(`BrowserStack Accessibility Automation Test Run ID: ${response.data.id}`) + if (response.data) { + AccessibilityScripts.update(response.data) + AccessibilityScripts.store() + } + return response.data.scannerVersion } catch (error : any) { if (error.response) { @@ -478,6 +485,27 @@ export const createAccessibilityTestRun = errorHandler(async function createAcce } }) +export const performA11yScan = async (browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser, isBrowserStackSession?: boolean, isAccessibility?: boolean | string, commandName?: string) : Promise<{ [key: string]: any; } | undefined> => { + if (!isBrowserStackSession) { + BStackLogger.warn('Not a BrowserStack Automate session, cannot perform Accessibility scan.') + return // since we are running only on Automate as of now + } + + if (!isAccessibilityAutomationSession(isAccessibility)) { + BStackLogger.warn('Not an Accessibility Automation session, cannot perform Accessibility scan.') + return + } + + try { + const results: unknown = await (browser as WebdriverIO.Browser).executeAsync(AccessibilityScripts.performScan as string, { 'method': commandName || '' }) + BStackLogger.debug(util.format(results as string)) + return ( results as { [key: string]: any; } | undefined ) + } catch (err : any) { + BStackLogger.error('Accessibility Scan could not be performed : ' + err) + return + } +} + export const getA11yResults = async (browser: WebdriverIO.Browser, isBrowserStackSession?: boolean, isAccessibility?: boolean | string) : Promise> => { if (!isBrowserStackSession) { BStackLogger.warn('Not a BrowserStack Automate session, cannot retrieve Accessibility results.') @@ -490,7 +518,9 @@ export const getA11yResults = async (browser: WebdriverIO.Browser, isBrowserStac } try { - const results = await (browser as WebdriverIO.Browser).execute(accessibilityResults) + BStackLogger.debug('Performing scan before getting results') + await performA11yScan(browser, isBrowserStackSession, isAccessibility) + const results: Array<{ [key: string]: any; }> = await (browser as WebdriverIO.Browser).executeAsync(AccessibilityScripts.getResults as string) return results } catch { BStackLogger.error('No accessibility results were found.') @@ -509,7 +539,9 @@ export const getA11yResultsSummary = async (browser: WebdriverIO.Browser, isBrow } try { - const summaryResults = await (browser as WebdriverIO.Browser).execute(accessibilityResultsSummary) + BStackLogger.debug('Performing scan before getting results summary') + await performA11yScan(browser, isBrowserStackSession, isAccessibility) + const summaryResults: { [key: string]: any; } = await (browser as WebdriverIO.Browser).executeAsync(AccessibilityScripts.getResultsSummary as string) return summaryResults } catch { BStackLogger.error('No accessibility summary was found.') @@ -1222,4 +1254,3 @@ export const getErrorString = (err: unknown) => { return err.message // works, `e` narrowed to Error } } - diff --git a/packages/wdio-browserstack-service/tests/accessibility-handler.test.ts b/packages/wdio-browserstack-service/tests/accessibility-handler.test.ts index d01f01dcfae..bbb1e991287 100644 --- a/packages/wdio-browserstack-service/tests/accessibility-handler.test.ts +++ b/packages/wdio-browserstack-service/tests/accessibility-handler.test.ts @@ -181,8 +181,7 @@ describe('beforeScenario', () => { } } as any) - expect(executeAsyncSpy).toBeCalledTimes(1) - expect(logInfoMock.mock.calls[1][0]) + expect(logInfoMock.mock.calls[0][0]) .toContain('Automate test case execution has started.') }) @@ -228,7 +227,7 @@ describe('beforeScenario', () => { } } as any) - expect(executeSpy).toBeCalledTimes(1) + expect(executeSpy).toBeCalledTimes(0) }) it('should not execute test started if shouldRunTestHooks is false', async () => { @@ -254,7 +253,7 @@ describe('beforeScenario', () => { const logErrorMock = vi.spyOn(log, 'error') vi.spyOn(utils, 'shouldScanTestForAccessibility').mockReturnValue(true) accessibilityHandler['shouldRunTestHooks'] = vi.fn().mockImplementation(() => { return true }) - accessibilityHandler['sendTestStartEvent'] = vi.fn().mockImplementation(() => { throw new Error() }) + accessibilityHandler['checkIfPageOpened'] = vi.fn().mockImplementation(() => { throw new Error() }) await accessibilityHandler.beforeScenario({ pickle: { name: 'pickle-name', @@ -270,7 +269,7 @@ describe('beforeScenario', () => { } as any) expect(logErrorMock.mock.calls[0][0]) - .toContain('Exception in starting accessibility automation scan for this test case Error') + .toContain('Exception in starting accessibility automation scan for this test case') }) }) @@ -395,12 +394,10 @@ describe('beforeTest', () => { it('should execute test started if page opened and can scan the page', async () => { const logInfoMock = vi.spyOn(log, 'info') vi.spyOn(utils, 'shouldScanTestForAccessibility').mockReturnValue(true) - accessibilityHandler['sendTestStartEvent'] = vi.fn().mockImplementation(() => { return [] }) await accessibilityHandler.beforeTest('suite title', { parent: 'parent', title: 'test' } as any) - expect(accessibilityHandler['sendTestStartEvent']).toBeCalledTimes(1) - expect(logInfoMock.mock.calls[1][0]) + expect(logInfoMock.mock.calls[0][0]) .toContain('Automate test case execution has started.') vi.fn().mockRestore() }) @@ -421,7 +418,7 @@ describe('beforeTest', () => { vi.spyOn(utils, 'shouldScanTestForAccessibility').mockReturnValue(false) await accessibilityHandler.beforeTest('suite title', { parent: 'parent', title: 'test' } as any) - expect(executeSpy).toBeCalledTimes(1) + expect(executeSpy).toBeCalledTimes(0) }) it('should not execute test started if shouldRunTestHooks is false', async () => { @@ -434,8 +431,7 @@ describe('beforeTest', () => { it('should throw error in before test if exception occurs', async () => { const logErrorMock = vi.spyOn(log, 'error') vi.spyOn(utils, 'shouldScanTestForAccessibility').mockReturnValue(true) - accessibilityHandler['shouldRunTestHooks'] = vi.fn().mockImplementation(() => { return true }) - accessibilityHandler['sendTestStartEvent'] = vi.fn().mockImplementation(() => { throw new Error() }) + accessibilityHandler['shouldRunTestHooks'] = vi.fn().mockImplementation(() => { throw new Error() }) await accessibilityHandler.beforeTest('suite title', { parent: 'parent', title: 'test' } as any) expect(logErrorMock.mock.calls[0][0]) diff --git a/packages/wdio-browserstack-service/tests/accessibility-scripts.test.ts b/packages/wdio-browserstack-service/tests/accessibility-scripts.test.ts new file mode 100644 index 00000000000..2061bba843f --- /dev/null +++ b/packages/wdio-browserstack-service/tests/accessibility-scripts.test.ts @@ -0,0 +1,82 @@ +import fs from 'node:fs' + +import { describe, expect, it, vi, afterAll, beforeAll } from 'vitest' + +import AccessibilityScripts from '../src/scripts/accessibility-scripts.js' + +vi.mock('node:fs', () => ({ + default: { + readFileSync: vi.fn().mockReturnValue('{"scripts": {"scan": "scan", "getResults": "getResults", "getResultsSummary": "getResultsSummary", "saveResults": "saveResults"}, "commands": [{"command": "command1"}, {"command": "command2"}]}'), + writeFileSync: vi.fn(), + existsSync: vi.fn().mockReturnValue(true), + mkdirSync: vi.fn() + } +})) + +describe('AccessibilityScripts', () => { + let accessibilityScripts: typeof AccessibilityScripts + + beforeAll(() => { + accessibilityScripts = AccessibilityScripts + }) + + afterAll(() => { + accessibilityScripts.store() + }) + + it('should read from existing file if it exists', () => { + // Simulate existing commands.json file + accessibilityScripts.readFromExistingFile() + + expect(accessibilityScripts.performScan).to.equal('scan') + expect(accessibilityScripts.getResults).to.equal('getResults') + expect(accessibilityScripts.getResultsSummary).to.equal('getResultsSummary') + expect(accessibilityScripts.saveTestResults).to.equal('saveResults') + expect(accessibilityScripts.commandsToWrap).to.deep.equal([{ command: 'command1' }, { command: 'command2' }]) + }) + + it('should update data', () => { + const data = { + commands: [{ command: 'command1' }, { command: 'command2' }], + scripts: { + scan: 'scan', + getResults: 'getResults', + getResultsSummary: 'getResultsSummary', + saveResults: 'saveResults', + }, + } as unknown + + accessibilityScripts.update(data as { commands: [any]; scripts: { scan: null; getResults: null; getResultsSummary: null; saveResults: null } }) + + expect(accessibilityScripts.performScan).to.equal('scan') + expect(accessibilityScripts.getResults).to.equal('getResults') + expect(accessibilityScripts.getResultsSummary).to.equal('getResultsSummary') + expect(accessibilityScripts.saveTestResults).to.equal('saveResults') + expect(accessibilityScripts.commandsToWrap).to.deep.equal([{ command: 'command1' }, { command: 'command2' }]) + }) + + it('should store data to file', () => { + // Mock storing data + accessibilityScripts.performScan = 'scan' + accessibilityScripts.getResults = 'getResults' + accessibilityScripts.getResultsSummary = 'getResultsSummary' + accessibilityScripts.saveTestResults = 'saveResults' + accessibilityScripts.commandsToWrap = [{ command: 'command1' }, { command: 'command2' }] + + const writeFileSyncStub = vi.spyOn(fs, 'writeFileSync') + accessibilityScripts.store() + // Check if the correct data is being written to the file + expect(writeFileSyncStub).toHaveBeenCalledWith( + accessibilityScripts.commandsPath, + JSON.stringify({ + commands: accessibilityScripts.commandsToWrap, + scripts: { + scan: accessibilityScripts.performScan, + getResults: accessibilityScripts.getResults, + getResultsSummary: accessibilityScripts.getResultsSummary, + saveResults: accessibilityScripts.saveTestResults, + } + }) + ) + }) +}) diff --git a/packages/wdio-browserstack-service/tests/util.test.ts b/packages/wdio-browserstack-service/tests/util.test.ts index 549681f5961..1c9bfcb0fd1 100644 --- a/packages/wdio-browserstack-service/tests/util.test.ts +++ b/packages/wdio-browserstack-service/tests/util.test.ts @@ -54,6 +54,9 @@ vi.mock('fs', () => ({ createReadStream: vi.fn().mockImplementation(() => {return { pipe: vi.fn().mockReturnThis() }}), createWriteStream: vi.fn().mockReturnValue({ pipe: vi.fn() }), stat: vi.fn().mockReturnValue(Promise.resolve({ size: 123 })), + existsSync: vi.fn(), + mkdirSync: vi.fn(), + writeFileSync: vi.fn() } })) @@ -1047,7 +1050,7 @@ describe('createAccessibilityTestRun', () => { json: () => Promise.resolve({ data: { accessibilityToken: 'someToken', id: 'id', scannerVersion: '0.0.6.0' } }), } as any) - const result: any = await createAccessibilityTestRun( { framework: 'framework' } as any, { user: 'user', key: 'key' }, {}) + const result: any = await createAccessibilityTestRun( { framework: 'framework' } as any, { user: 'user', key: 'key' }, { bstackServiceVersion: '1.2.3' }) expect(got).toBeCalledTimes(1) expect(result).toEqual('0.0.6.0') }) @@ -1061,7 +1064,7 @@ describe('createAccessibilityTestRun', () => { const result: any = await createAccessibilityTestRun( { framework: 'framework' } as any, { user: 'user', key: 'key' }, {}) expect(got).toBeCalledTimes(1) expect(result).toEqual(null) - expect(logInfoMock.mock.calls[3][0]).contains('xception while creating test run for BrowserStack Accessibility Automation') + expect(logInfoMock.mock.calls[3][0]).contains('Exception while creating test run for BrowserStack Accessibility Automation') }) afterEach(() => { @@ -1127,7 +1130,7 @@ describe('getA11yResults', () => { getInstance: vi.fn().mockImplementation((browserName: string) => browser[browserName]), browserB: {}, execute: vi.fn(), - executeAsync: async () => { 'done' }, + executeAsync: vi.fn(), on: vi.fn(), } as any as WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser @@ -1145,7 +1148,7 @@ describe('getA11yResults', () => { it('return results object if bstack as well as accessibility session', async () => { vi.spyOn(utils, 'isAccessibilityAutomationSession').mockReturnValue(true) await utils.getA11yResults((browser as WebdriverIO.Browser), true, true) - expect(browser.execute).toBeCalledTimes(1) + expect(browser.executeAsync).toBeCalledTimes(2) }) }) @@ -1173,7 +1176,7 @@ describe('getA11yResultsSummary', () => { getInstance: vi.fn().mockImplementation((browserName: string) => browser[browserName]), browserB: {}, execute: vi.fn(), - executeAsync: async () => { 'done' }, + executeAsync: vi.fn(), on: vi.fn(), } as any as WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser @@ -1191,7 +1194,7 @@ describe('getA11yResultsSummary', () => { it('return results object if bstack as well as accessibility session', async () => { vi.spyOn(utils, 'isAccessibilityAutomationSession').mockReturnValue(true) await utils.getA11yResultsSummary((browser as WebdriverIO.Browser), true, true) - expect(browser.execute).toBeCalledTimes(1) + expect(browser.executeAsync).toBeCalledTimes(2) }) })