forked from webdriverio/webdriverio
-
Notifications
You must be signed in to change notification settings - Fork 0
Wdio Percy support v7 #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
amaanbs
wants to merge
45
commits into
v7
Choose a base branch
from
wdio_percy_support_v7
base: v7
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
32807ed
add percy support for wdio v7
amaanbs f14e3bc
ts-lint fixes
amaanbs b3d6b89
minor fixes
amaanbs 63a0001
minor fixes 2
amaanbs cf5d47c
PR review fixes
amaanbs 29daea0
add unit tests
amaanbs 883d46e
remove unused imports
amaanbs 2d6ebe8
minor fixes + add package-lock.json
amaanbs 0af2198
ts-lint fixes
amaanbs 6e084d4
Merge branch 'v7' of github.com:amaanbs/webdriverio into wdio_percy_s…
amaanbs 8d99528
Stabilization + PR review fixes
amaanbs f7fff5e
master merge and conflict resolution
rev-doshi d648b98
fixes related to types, return early and private fields
rev-doshi ed84de3
master merge
rev-doshi d6dc372
Merge branch 'v7' of github.com:amaanbs/webdriverio into wdio_percy_s…
rev-doshi 0121dc1
master merge
rev-doshi 6f7e838
fixes
rev-doshi b5a2b16
resolved dependency check
rev-doshi de8c057
review changes
rev-doshi d25d9fa
review changes
rev-doshi ff064d1
test fixes
rev-doshi 83767e4
test fixes
rev-doshi ec06ee8
Merge pull request #6 from rev-doshi/wdio_percy_support_v7_review_fixes
rev-doshi fdb0e15
added percy logger tests
rev-doshi 845dc69
added percy logger tests
rev-doshi cce2d27
added linting fix
rev-doshi b3ff1fc
percy handler tests
rev-doshi 48dc775
linting fix
rev-doshi 9272da3
percy capture map tests
rev-doshi d70cef6
linting fix
rev-doshi 13d78ca
linting fix
rev-doshi c14b1f1
percy test UTs added
rev-doshi 60229e2
tests and minor fixes
rev-doshi 8d3ac0e
linting fix
rev-doshi 5c28b50
percy test fixes
rev-doshi 9dbc50f
percy test fixes
rev-doshi 86b507e
percy test added
rev-doshi 1ca8aa1
tests for utils and launcher
rev-doshi 07d143f
tests fix for launcher
rev-doshi dec59da
tests fix
rev-doshi cf64e96
tests fix
rev-doshi d77dd45
tests fix
rev-doshi f56cadc
tests fix
rev-doshi 26ac1f5
coverage threshold change
rev-doshi de7b507
Merge branch 'v7' of github.com:amaanbs/webdriverio into wdio_percy_s…
rev-doshi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
packages/wdio-browserstack-service/src/Percy/Percy-Handler.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import type { Capabilities } from '@wdio/types' | ||
| import type { BeforeCommandArgs, AfterCommandArgs } from '@wdio/reporter' | ||
| import type { Browser, MultiRemoteBrowser } from 'webdriverio' | ||
|
|
||
| import { | ||
| o11yClassErrorHandler | ||
| } from '../util' | ||
| import PercyCaptureMap from './PercyCaptureMap' | ||
|
|
||
| import * as PercySDK from './PercySDK' | ||
| import { PercyLogger } from './PercyLogger' | ||
| import { BrowserAsync } from 'src/@types/bstack-service-types' | ||
|
|
||
| class _PercyHandler { | ||
| private _testMetadata: { [key: string]: any } = {} | ||
| private sessionName?: string | ||
| private _isAppAutomate?: boolean | ||
| public _percyScreenshotCounter: any = 0 | ||
|
|
||
| constructor ( | ||
| private _percyAutoCaptureMode: string | undefined, | ||
| private _browser: Browser<'async'> | MultiRemoteBrowser<'async'>, | ||
| private _capabilities: Capabilities.RemoteCapability, | ||
| isAppAutomate?: boolean, | ||
| private _framework?: string | ||
| ) { | ||
| this._isAppAutomate = isAppAutomate | ||
| } | ||
|
|
||
| _setSessionName(name: string) { | ||
| this.sessionName = name | ||
| } | ||
|
|
||
| async teardown () { | ||
| await new Promise<void>((resolve) => { | ||
| setInterval(() => { | ||
| if (this._percyScreenshotCounter === 0) { | ||
| resolve() | ||
| } | ||
| }, 1000) | ||
| }) | ||
| } | ||
|
|
||
| async percyAutoCapture(eventName: string | null) { | ||
| try { | ||
| if (eventName) { | ||
| this._percyScreenshotCounter += 1 | ||
| this._isAppAutomate ? await PercySDK.screenshotApp(this._browser, ((this._browser as BrowserAsync).percyCaptureMap as PercyCaptureMap).getName((this.sessionName as string), eventName)) : await PercySDK.screenshot(this._browser, ((this._browser as BrowserAsync).percyCaptureMap as PercyCaptureMap).getName((this.sessionName as string), eventName)); | ||
| ((this._browser as BrowserAsync).percyCaptureMap as PercyCaptureMap).increment((this.sessionName as string), eventName) | ||
| this._percyScreenshotCounter -= 1 | ||
| } | ||
| } catch (err: any) { | ||
| PercyLogger.error(`Error while trying to auto capture Percy screenshot ${err}`) | ||
| } | ||
| } | ||
|
|
||
| async before () { | ||
| (this._browser as BrowserAsync).percyCaptureMap = new PercyCaptureMap() | ||
| } | ||
|
|
||
| async browserCommand (args: BeforeCommandArgs & AfterCommandArgs) { | ||
| try { | ||
| if (args.endpoint && this._percyAutoCaptureMode) { | ||
| let eventName = null | ||
| if ((args.endpoint as string).includes('click') && ['click', 'auto'].includes(this._percyAutoCaptureMode as string)) { | ||
| eventName = 'click' | ||
| } else if ((args.endpoint as string).includes('screenshot') && ['screenshot', 'auto'].includes(this._percyAutoCaptureMode as string)) { | ||
| eventName = 'screenshot' | ||
| } else if ((args.endpoint as string).includes('actions') && ['auto'].includes(this._percyAutoCaptureMode as string)) { | ||
| if (args.body && args.body.actions && Array.isArray(args.body.actions) && args.body.actions.length && args.body.actions[0].type === 'key') { | ||
| eventName = 'keys' | ||
| } | ||
| } | ||
| await this.percyAutoCapture(eventName) | ||
| } | ||
| } catch (err: any) { | ||
| PercyLogger.error(`Error while trying to calculate auto capture parameters ${err}`) | ||
| } | ||
| } | ||
|
|
||
| async afterTest () { | ||
| if (this._percyAutoCaptureMode && this._percyAutoCaptureMode === 'testcase') { | ||
| await this.percyAutoCapture('testcase') | ||
| } | ||
| } | ||
|
|
||
| async afterScenario () { | ||
| if (this._percyAutoCaptureMode && this._percyAutoCaptureMode === 'testcase') { | ||
| await this.percyAutoCapture('testcase') | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // https://github.com/microsoft/TypeScript/issues/6543 | ||
| const PercyHandler: typeof _PercyHandler = o11yClassErrorHandler(_PercyHandler) | ||
| type PercyHandler = _PercyHandler | ||
|
|
||
| export default PercyHandler | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import fs from 'node:fs' | ||
| import path from 'node:path' | ||
| import os from 'node:os' | ||
|
|
||
| import { spawn } from 'node:child_process' | ||
|
|
||
| import { nodeRequest, getBrowserStackUser, getBrowserStackKey } from '../util' | ||
| import { PercyLogger } from './PercyLogger' | ||
|
|
||
| import PercyBinary from './PercyBinary' | ||
|
|
||
| import type { BrowserstackConfig, UserConfig } from '../types' | ||
| import type { Options } from '@wdio/types' | ||
|
|
||
| const logDir = 'logs' | ||
|
|
||
| class Percy { | ||
| #logfile: string = path.join(logDir, 'percy.log') | ||
| #address: string = process.env.PERCY_SERVER_ADDRESS || 'http://localhost:5338' | ||
|
|
||
| #binaryPath: string | any = null | ||
| #options: BrowserstackConfig & Options.Testrunner | ||
| #config: Options.Testrunner | ||
| #proc: any = null | ||
| #isApp: boolean = false | ||
| #projectName: string | undefined = undefined | ||
|
|
||
| isProcessRunning = false | ||
|
|
||
| constructor(options: BrowserstackConfig & Options.Testrunner, config: Options.Testrunner, bsConfig: UserConfig) { | ||
| this.#options = options | ||
| this.#config = config | ||
| if (options.app) { | ||
| this.#isApp = true | ||
| } | ||
| this.#projectName = bsConfig.projectName | ||
| } | ||
|
|
||
| async #getBinaryPath(): Promise<string> { | ||
| if (!this.#binaryPath) { | ||
| const pb = new PercyBinary() | ||
| this.#binaryPath = await pb.getBinaryPath(this.#config) | ||
| } | ||
| return this.#binaryPath | ||
| } | ||
|
|
||
| async #sleep(ms: number) { | ||
| return new Promise(resolve => setTimeout(resolve, ms)) | ||
| } | ||
|
|
||
| async healthcheck() { | ||
| try { | ||
| const resp = await nodeRequest('GET', 'percy/healthcheck', null, this.#address) | ||
| if (resp) { | ||
| return true | ||
| } | ||
| } catch (err) { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| async start() { | ||
| const binaryPath: string = await this.#getBinaryPath() | ||
| const logStream = fs.createWriteStream(this.#logfile, { flags: 'a' }) | ||
| const token = await this.fetchPercyToken() | ||
| const configPath = await this.createPercyConfig() | ||
|
|
||
| if (!token) { | ||
| return false | ||
| } | ||
|
|
||
| const commandArgs = [`${this.#isApp ? 'app:exec' : 'exec'}:start`] | ||
|
|
||
| if (configPath) { | ||
| commandArgs.push('-c', configPath as string) | ||
| } | ||
|
|
||
| this.#proc = spawn( | ||
| binaryPath, | ||
| commandArgs, | ||
| { env: Object.assign(process.env, { PERCY_TOKEN: token }) } | ||
| ) | ||
|
|
||
| this.#proc.stdout.pipe(logStream) | ||
| this.#proc.stderr.pipe(logStream) | ||
| this.isProcessRunning = true | ||
| const that = this | ||
|
|
||
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
| this.#proc.on('close', function (code: any) { | ||
| that.isProcessRunning = false | ||
| }) | ||
|
|
||
| do { | ||
| const healthcheck = await this.healthcheck() | ||
| if (healthcheck) { | ||
| PercyLogger.debug('Percy healthcheck successful') | ||
| return true | ||
| } | ||
|
|
||
| await this.#sleep(1000) | ||
| } while (this.isProcessRunning) | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| async stop() { | ||
| const binaryPath = await this.#getBinaryPath() | ||
| return new Promise( (resolve) => { | ||
| const proc = spawn(binaryPath, ['exec:stop']) | ||
| proc.on('close', (code: any) => { | ||
| this.isProcessRunning = false | ||
| resolve(code) | ||
| }) | ||
| }) | ||
| } | ||
|
|
||
| isRunning() { | ||
| return this.isProcessRunning | ||
| } | ||
|
|
||
| async fetchPercyToken() { | ||
| const projectName = this.#projectName | ||
|
|
||
| try { | ||
| const type = this.#isApp ? 'app' : 'automate' | ||
| const response: any = await nodeRequest( | ||
| 'GET', | ||
| `api/app_percy/get_project_token?name=${projectName}&type=${type}`, | ||
| { | ||
| username: getBrowserStackUser(this.#config), | ||
| password: getBrowserStackKey(this.#config) | ||
| }, | ||
| 'https://api.browserstack.com' | ||
| ) | ||
| PercyLogger.debug('Percy fetch token success : ' + response.token) | ||
| return response.token | ||
| } catch (err: any) { | ||
| PercyLogger.error(`Percy unable to fetch project token: ${err}`) | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| async createPercyConfig() { | ||
| if (!this.#options.percyOptions) { | ||
| return null | ||
| } | ||
|
|
||
| const configPath = path.join(os.tmpdir(), 'percy.json') | ||
| const percyOptions = this.#options.percyOptions | ||
|
|
||
| if (!percyOptions.version) { | ||
| percyOptions.version = '2' | ||
| } | ||
|
|
||
| return new Promise((resolve) => { | ||
| fs.writeFile( | ||
| configPath, | ||
| JSON.stringify( | ||
| percyOptions | ||
| ), | ||
| (err: any) => { | ||
| if (err) { | ||
| PercyLogger.error(`Error creating percy config: ${err}`) | ||
| resolve(null) | ||
| } | ||
|
|
||
| PercyLogger.debug('Percy config created at ' + configPath) | ||
| resolve(configPath) | ||
| } | ||
| ) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| export default Percy |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.