diff --git a/.changeset/pr-263.md b/.changeset/pr-263.md new file mode 100644 index 00000000..97690060 --- /dev/null +++ b/.changeset/pr-263.md @@ -0,0 +1,5 @@ +--- +"@wdio/browserstack-service": patch +--- + +- Fixed `ETXTBSY: text file is busy, open '~/.browserstack/percy'` crashes on Linux when Percy is enabled. diff --git a/packages/browserstack-service/src/launcher.ts b/packages/browserstack-service/src/launcher.ts index a5829bc0..d9c95f02 100644 --- a/packages/browserstack-service/src/launcher.ts +++ b/packages/browserstack-service/src/launcher.ts @@ -515,11 +515,16 @@ export default class BrowserstackLauncherService implements Services.ServiceInst try { const bestPlatformPercyCaps = getBestPlatformForPercySnapshot(capabilities as Capabilities.TestrunnerCapabilities) this._percyBestPlatformCaps = bestPlatformPercyCaps as WebdriverIO.Capabilities - process.env[BROWSERSTACK_PERCY] = 'false' - await this.setupPercy(this._options, this._config, { - projectName: this._projectName - }) - this._updateBrowserStackPercyConfig() + // The CLI runs Percy from the same path; re-downloading over the running executable fails with ETXTBSY on Linux. + if (BrowserstackCLI.getInstance().isRunning()) { + PercyLogger.debug('Percy is managed by the BrowserStack CLI, skipping service-side Percy setup') + } else { + process.env[BROWSERSTACK_PERCY] = 'false' + await this.setupPercy(this._options, this._config, { + projectName: this._projectName + }) + this._updateBrowserStackPercyConfig() + } } catch (err) { PercyLogger.error(`Error while setting up Percy ${err}`) } diff --git a/packages/browserstack-service/tests/launcher.test.ts b/packages/browserstack-service/tests/launcher.test.ts index 9bbb24ac..f6216c7a 100644 --- a/packages/browserstack-service/tests/launcher.test.ts +++ b/packages/browserstack-service/tests/launcher.test.ts @@ -151,6 +151,32 @@ describe('onPrepare', () => { expect(service.browserstackLocal).toBeUndefined() }) + it('does not start a service-side Percy when the CLI is running', async () => { + const isRunningSpy = vi.spyOn(BrowserstackCLI.getInstance(), 'isRunning').mockReturnValue(true) + const service = new BrowserstackLauncher({ testObservability: false, percy: true } as any, caps, config) + const setupPercySpy = vi.spyOn(service, 'setupPercy').mockResolvedValue(undefined) + try { + await service.onPrepare(config, caps) + } finally { + isRunningSpy.mockRestore() + } + + expect(setupPercySpy).not.toHaveBeenCalled() + }) + + it('starts a service-side Percy when the CLI is not running', async () => { + const isRunningSpy = vi.spyOn(BrowserstackCLI.getInstance(), 'isRunning').mockReturnValue(false) + const service = new BrowserstackLauncher({ testObservability: false, percy: true } as any, caps, config) + const setupPercySpy = vi.spyOn(service, 'setupPercy').mockResolvedValue(undefined) + try { + await service.onPrepare(config, caps) + } finally { + isRunningSpy.mockRestore() + } + + expect(setupPercySpy).toHaveBeenCalledTimes(1) + }) + it('should add the "app" property to a multiremote capability if no "bstack:options"', async () => { const options: BrowserstackConfig = { app: 'bs://' } const service = new BrowserstackLauncher(options as any, caps, config)