Repository: webdriverio/webdriverio
Pull request #14454: feat(reporter): add browserName function and use where required
Author: login: Delta456
Labels: name: PR: Polish :nail_care:
description: PRs that contain improvements on existing features
Created: 2025-05-05T14:53:05Z | Merged: 2025-05-06T15:05:04Z

## Proposed changes

[//]: # (Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.)

Provide a way to get browser or app name based on the passed capabilities and allow other reporters to use it

Closes https://github.com/webdriverio/webdriverio/issues/14446

## Types of changes

[//]: # (What types of changes does your code introduce to WebdriverIO?)
[//]: # (_Put an `x` in the boxes that apply_)

- [x] Polish (an improvement for an existing feature)
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update (improvements to the project's docs)
- [ ] Specification changes (updates to WebDriver command specifications)
- [ ] Internal updates (everything related to internal scripts, governance documentation and CI files)

## Checklist

[//]: # (_Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._)

- [x] I have read the [CONTRIBUTING](https://github.com/webdriverio/webdriverio/blob/main/CONTRIBUTING.md) doc
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] I have added the necessary documentation (if appropriate)
- [ ] I have added proper type definitions for new commands (if appropriate)

## Backport Request

[//]: # (The current `main` branch is the development branch for WebdriverIO v9. If your change should be released to the current major version of WebdriverIO (v8), please raise another PR with the same changes against the `v8` branch.)

- [ ] This change is solely for `v9` and doesn't need to be back-ported
- [ ] Back-ported PR at `#XXXXX`

## Further comments

[//]: # (If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...)

### Reviewers: @webdriverio/project-committers



Diff:
diff --git a/__mocks__/@wdio/reporter.ts b/__mocks__/@wdio/reporter.ts
index 13de770fedc..2de6dcfb762 100644
--- a/__mocks__/@wdio/reporter.ts
+++ b/__mocks__/@wdio/reporter.ts
@@ -81,5 +81,6 @@ export default class WDIOReporter extends EventEmitter {
     /* istanbul ignore next */
     onRunnerEnd () {}
 }
+const { getBrowserName } = await vi.importActual<typeof import('../../packages/wdio-reporter/src/index')>('../../packages/wdio-reporter/src/index')
 
-export { HookStats, RunnerStats, SuiteStats, TestStats }
+export { HookStats, RunnerStats, SuiteStats, TestStats, getBrowserName }
diff --git a/packages/wdio-allure-reporter/src/reporter.ts b/packages/wdio-allure-reporter/src/reporter.ts
index d8709ffda5f..c60c8740ce6 100644
--- a/packages/wdio-allure-reporter/src/reporter.ts
+++ b/packages/wdio-allure-reporter/src/reporter.ts
@@ -3,6 +3,7 @@ import type {
     SuiteStats, HookStats, RunnerStats, TestStats, BeforeCommandArgs,
     AfterCommandArgs, Argument
 } from '@wdio/reporter'
+import { getBrowserName } from '@wdio/reporter'
 import WDIOReporter from '@wdio/reporter'
 import type { Capabilities, Options } from '@wdio/types'
 import type { Label, MetadataMessage } from 'allure-js-commons'
@@ -243,10 +244,10 @@ export default class AllureReporter extends WDIOReporter {
         if (!this._isMultiremote) {
             const caps = this._capabilities
             // @ts-expect-error outdated JSONWP capabilities
-            const { browserName, desired, device } = caps
+            const { desired, device } = caps
             // @ts-expect-error outdated JSONWP capabilities
             const deviceName = (desired || {}).deviceName || (desired || {})['appium:deviceName'] || caps.deviceName || caps['appium:deviceName']
-            let targetName = device || browserName || deviceName || cid
+            let targetName = device || getBrowserName(caps) || deviceName || cid
             // custom mobile grids can have device information in a `desired` cap
             if (desired && deviceName && desired['appium:platformVersion']) {
                 targetName = `${device || deviceName} ${desired['appium:platformVersion']}`
diff --git a/packages/wdio-concise-reporter/src/index.ts b/packages/wdio-concise-reporter/src/index.ts
index 0315cb6774d..44747bd2672 100644
--- a/packages/wdio-concise-reporter/src/index.ts
+++ b/packages/wdio-concise-reporter/src/index.ts
@@ -1,4 +1,4 @@
-import type { SuiteStats, RunnerStats } from '@wdio/reporter'
+import { getBrowserName, type SuiteStats, type RunnerStats } from '@wdio/reporter'
 import WDIOReporter from '@wdio/reporter'
 import chalk from 'chalk'
 
@@ -105,8 +105,7 @@ export default class ConciseReporter extends WDIOReporter {
     getEnviromentCombo (caps: WebdriverIO.Capabilities) {
         // @ts-expect-error `deviceName` and `device` are outdated JSONWP caps
         const device = caps.deviceName || caps['appium:deviceName'] || caps.device
-        // @ts-expect-error `browser` is an outdated JSONWP cap
-        const browser = caps.browserName || caps.browser
+        const browser = getBrowserName(caps)
         // @ts-expect-error `version` and `browser_version` are outdated JSONWP caps
         const version = caps.browserVersion || caps.version || caps['appium:platformVersion'] || caps.browser_version
         // @ts-expect-error `os`, `os_version` and `platform` are outdated JSONWP caps
diff --git a/packages/wdio-reporter/src/index.ts b/packages/wdio-reporter/src/index.ts
index c23b34527f1..ec1533780b1 100644
--- a/packages/wdio-reporter/src/index.ts
+++ b/packages/wdio-reporter/src/index.ts
@@ -1,4 +1,5 @@
 import fs from 'node:fs'
+import path from 'node:path'
 import type { WriteStream } from 'node:fs'
 import { EventEmitter } from 'node:events'
 import type { Reporters, Options } from '@wdio/types'
@@ -293,7 +294,25 @@ export default class WDIOReporter extends EventEmitter {
     onRunnerEnd(_runnerStats: RunnerStats) { }
 }
 
+/**
+ * Returns the browser name from the capabilities
+ * @param caps WebdriverIO Capabilities
+ * @returns {string} Browser name
+ */
+function getBrowserName(caps: WebdriverIO.Capabilities) {
+    // @ts-expect-error outdated JSONWP capabilities
+    const app = ((caps['appium:app'] || caps.app) || '').replace('sauce-storage:', '')
+    const appName = (
+        caps['appium:bundleId'] ||
+        caps['appium:appPackage'] ||
+        caps['appium:appActivity'] ||
+        (path.isAbsolute(app) ? path.basename(app) : app)
+    )
+    // @ts-expect-error outdated JSONWP capabilities
+    return caps.browserName || caps.browser || appName
+}
+
 export {
     SuiteStats, Tag, HookStats, TestStats, RunnerStats, BeforeCommandArgs,
-    AfterCommandArgs, CommandArgs, Argument, Test
+    AfterCommandArgs, CommandArgs, Argument, Test, getBrowserName
 }
diff --git a/packages/wdio-spec-reporter/src/index.ts b/packages/wdio-spec-reporter/src/index.ts
index 833832f711e..1129e3435d7 100644
--- a/packages/wdio-spec-reporter/src/index.ts
+++ b/packages/wdio-spec-reporter/src/index.ts
@@ -1,10 +1,9 @@
-import path from 'node:path'
 import { format } from 'node:util'
 
 import prettyMs from 'pretty-ms'
 import type { Capabilities } from '@wdio/types'
 import { Chalk, type ChalkInstance } from 'chalk'
-import WDIOReporter, { TestStats } from '@wdio/reporter'
+import WDIOReporter, { TestStats, getBrowserName } from '@wdio/reporter'
 import type { SuiteStats, HookStats, RunnerStats, Argument } from '@wdio/reporter'
 import { buildTableData, printTable, getFormattedRows, sauceAuthenticationToken } from './utils.js'
 import { ChalkColors, type SpecReporterOptions, type TestLink, type StateCount, type Symbols, State } from './types.js'
@@ -660,16 +659,7 @@ export default class SpecReporter extends WDIOReporter {
         }
         const caps = 'alwaysMatch' in capability ? capability.alwaysMatch : capability
         const device = caps['appium:deviceName']
-        // @ts-expect-error outdated JSONWP capabilities
-        const app = ((caps['appium:app'] || caps.app) || '').replace('sauce-storage:', '')
-        const appName = (
-            caps['appium:bundleId'] ||
-            caps['appium:appPackage'] ||
-            caps['appium:appActivity'] ||
-            (path.isAbsolute(app) ? path.basename(app) : app)
-        )
-        // @ts-expect-error outdated JSONWP capabilities
-        const browser = caps.browserName || caps.browser || appName
+        const browser = getBrowserName(caps)
         /**
          * fallback to different capability types:
          * browserVersion: W3C format
@@ -690,7 +680,7 @@ export default class SpecReporter extends WDIOReporter {
 
         // Mobile capabilities
         if (device) {
-            const program = appName || caps.browserName
+            const program = getBrowserName(caps)
             const executing = program ? `executing ${program}` : ''
             if (!verbose) {
                 return `${device} ${platform} ${version}`


Reviews:
user: login: christian-bromann
body: Can we apply this function in the other reporter in this repository? I am sure there are some opportunities where we can replace some custom logic.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T15:04:10Z
user: login: christian-bromann
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-05T16:33:01Z
user: login: Delta456
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:33:48Z
user: login: christian-bromann
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T16:36:27Z
user: login: Delta456
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T08:34:30Z
user: login: christian-bromann
body: LGTM 👍 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-06T15:03:15Z

Review comments:
diff_hunk: @@ -243,13 +244,13 @@ export default class AllureReporter extends WDIOReporter {
         if (!this._isMultiremote) {
             const caps = this._capabilities
             // @ts-expect-error outdated JSONWP capabilities
-            const { browserName, desired, device } = caps
+            const { _, desired, device } = caps
path: packages/wdio-allure-reporter/src/reporter.ts
user: login: christian-bromann
body: ```suggestion
            const { desired, device } = caps
```
created_at: 2025-05-05T16:31:55Z
author_association: MEMBER
diff_hunk: @@ -1,4 +1,5 @@
 import type { SuiteStats, RunnerStats } from '@wdio/reporter'
+import { getBrowserName } from '@wdio/reporter'
path: packages/wdio-concise-reporter/src/index.ts
user: login: christian-bromann
body: ```suggestion
import { getBrowserName, type SuiteStats, type RunnerStats } from '@wdio/reporter'
```
created_at: 2025-05-05T16:32:19Z
author_association: MEMBER
diff_hunk: @@ -82,4 +83,15 @@ export default class WDIOReporter extends EventEmitter {
     onRunnerEnd () {}
 }
 
-export { HookStats, RunnerStats, SuiteStats, TestStats }
+function getBrowserName(caps: any) {
path: __mocks__/@wdio/reporter.ts
user: login: christian-bromann
body: No need to duplicate the logic, we can import the actual module through vitest
created_at: 2025-05-05T16:32:52Z
author_association: MEMBER
diff_hunk: @@ -82,4 +83,15 @@ export default class WDIOReporter extends EventEmitter {
     onRunnerEnd () {}
 }
 
-export { HookStats, RunnerStats, SuiteStats, TestStats }
+function getBrowserName(caps: any) {
path: __mocks__/@wdio/reporter.ts
user: login: Delta456
body: How will I do that? Would love tips 😄 
created_at: 2025-05-05T16:33:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -82,4 +83,15 @@ export default class WDIOReporter extends EventEmitter {
     onRunnerEnd () {}
 }
 
-export { HookStats, RunnerStats, SuiteStats, TestStats }
+function getBrowserName(caps: any) {
path: __mocks__/@wdio/reporter.ts
user: login: christian-bromann
body: see https://vitest.dev/api/vi.html#vi-importactual
created_at: 2025-05-05T16:36:27Z
author_association: MEMBER
diff_hunk: @@ -82,4 +83,15 @@ export default class WDIOReporter extends EventEmitter {
     onRunnerEnd () {}
 }
 
-export { HookStats, RunnerStats, SuiteStats, TestStats }
+function getBrowserName(caps: any) {
path: __mocks__/@wdio/reporter.ts
user: login: Delta456
body: Should be done, thanks for the tip
created_at: 2025-05-06T08:34:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,7 +1,8 @@
 import { stringify } from 'csv-stringify/sync'
 import type {
     SuiteStats, HookStats, RunnerStats, TestStats, BeforeCommandArgs,
-    AfterCommandArgs, Argument
+    AfterCommandArgs, Argument } from '@wdio/reporter'
+import { getBrowserName
 } from '@wdio/reporter'
path: packages/wdio-allure-reporter/src/reporter.ts
user: login: christian-bromann
body: ```suggestion
    AfterCommandArgs, Argument
} from '@wdio/reporter'
import { getBrowserName } from '@wdio/reporter'
```
created_at: 2025-05-06T15:01:51Z
author_association: MEMBER

PR comments:
user: login: pkg-pr-new[bot]
created_at: 2025-05-05T14:55:37Z
body: 
[Open in StackBlitz](https://pkg.pr.new/template/a9ebcc45-5ddd-4222-95a9-74085ebcb738)


<details><summary><b>eslint-plugin-wdio</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/eslint-plugin-wdio@14454
```

</p></details>
      
    

<details><summary><b>@wdio/allure-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/allure-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/appium-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/appium-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/browser-runner</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/browser-runner@14454
```

</p></details>
      
    

<details><summary><b>@wdio/browserstack-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/browserstack-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/cli</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/cli@14454
```

</p></details>
      
    

<details><summary><b>@wdio/concise-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/concise-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/config</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/config@14454
```

</p></details>
      
    

<details><summary><b>@wdio/cucumber-framework</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/cucumber-framework@14454
```

</p></details>
      
    

<details><summary><b>@wdio/dot-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/dot-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/firefox-profile-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/firefox-profile-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/globals</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/globals@14454
```

</p></details>
      
    

<details><summary><b>@wdio/jasmine-framework</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/jasmine-framework@14454
```

</p></details>
      
    

<details><summary><b>@wdio/json-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/json-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/junit-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/junit-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/lighthouse-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/lighthouse-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/local-runner</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/local-runner@14454
```

</p></details>
      
    

<details><summary><b>@wdio/logger</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/logger@14454
```

</p></details>
      
    

<details><summary><b>@wdio/mocha-framework</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/mocha-framework@14454
```

</p></details>
      
    

<details><summary><b>@wdio/protocols</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/protocols@14454
```

</p></details>
      
    

<details><summary><b>@wdio/repl</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/repl@14454
```

</p></details>
      
    

<details><summary><b>@wdio/reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/runner</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/runner@14454
```

</p></details>
      
    

<details><summary><b>@wdio/sauce-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/sauce-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/shared-store-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/shared-store-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/smoke-test-cjs-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/smoke-test-cjs-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/smoke-test-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/smoke-test-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/smoke-test-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/smoke-test-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/spec-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/spec-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/sumologic-reporter</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/sumologic-reporter@14454
```

</p></details>
      
    

<details><summary><b>@wdio/static-server-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/static-server-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/testingbot-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/testingbot-service@14454
```

</p></details>
      
    

<details><summary><b>@wdio/types</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/types@14454
```

</p></details>
      
    

<details><summary><b>@wdio/utils</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/utils@14454
```

</p></details>
      
    

<details><summary><b>@wdio/webdriver-mock-service</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/@wdio/webdriver-mock-service@14454
```

</p></details>
      
    

<details><summary><b>webdriver</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio/webdriver@14454
```

</p></details>
      
    

<details><summary><b>webdriverio</b></summary><p>

```
npm i https://pkg.pr.new/webdriverio/webdriverio@14454
```

</p></details>
      
    

_commit: <a href="https://github.com/webdriverio/webdriverio/runs/41733005895"><code>c4f26c0</code></a>_

author_association: NONE
user: login: Delta456
created_at: 2025-05-05T16:23:41Z
body: > Can we apply this function in the other reporter in this repository? I am sure there are some opportunities where we can replace some custom logic.

It should be done as discussed
author_association: CONTRIBUTOR