wdio-locator-scout-service
v0.3.0
Published
A WebdriverIO service that automatically generates UI locator candidates for Android and iOS apps.
Maintainers
Readme
WebdriverIO Locator Scout Service (v0.3.0)
A WebdriverIO service that automatically generates a helpful report of UI locator candidates for your Android and iOS app. Stop wasting time in Appium Inspector and let the scout find the best selectors for you!
Why use Locator Scout?
- Cross-Platform Support: Works seamlessly for both Android and iOS with automatic platform detection.
- Save Time: No more manual digging through XML or UI hierarchies in Appium Inspector.
- Best Practices: Automatically scores and prioritizes selectors based on industry best practices (
resource-id/name>content-desc/label). - Easy Debugging: Get a clear snapshot of the UI structure of any screen in your app.
Installation
npm install wdio-locator-scout-service --save-devConfiguration
Add the plugin to your wdio.conf.js services list. This will automatically add the browser.dumpLocators() command to your test environment.
// wdio.conf.js
export const config = {
// ...
services: [
// ... other services
"locator-scout",
],
// ...
};Usage
Once configured, the dumpLocators command is available on the browser object in your tests. The plugin automatically detects the platform (Android or iOS) and generates the appropriate report.
import OnboardingPage from "../../pageobjects/OnboardingPage";
describe("[Locator Scout] 로케이터 확인", async () => {
const platformName = await browser.capabilities.platformName;
it(`${platformName} 온보딩 화면 로케이터 확인`, async () => {
await browser.dumpLocators({
fileName: "onboarding_main_screen",
reportTitle: `${platformName} 온보딩 화면 로케이터 확인`,
outDir: "./reports/locator/onboarding_main_screen",
});
});
it(`${platformName} 시작 화면 로케이터 확인`, async () => {
await OnboardingPage.clickCheckAgreementButton();
await OnboardingPage.clickStartButton();
await browser.dumpLocators({
fileName: "start_main_screen",
reportTitle: `${platformName} 시작 화면 로케이터 확인`,
outDir: "./reports/locator/start_main_screen",
});
});
});Example Reports
See how Locator Scout provides optimized reports tailored for each platform.
- Device: ANDROID R3CN30JZMZX 13
- Scanned Elements: 12
- Generated: 11/28/2025, 3:03:52 PM
Note: ⚠️ indicates a shared ID (not unique).
1. Overview (Top Elements)
| # | Score | Context (Description) | Class | ID (Resource) | Content-Desc | Text | | --- | ----: | -------------------------------------------------------------------------- | -------- | ------------- | ------------------------------------- | ---------------------------------------------------------------------- | | 1 | 33 | 수면무호흡 진단 보조앱 Apnotrack | TextView | - | onboarding_title | 수면무호흡 진단 보조앱 Apnotrack | | 2 | 33 | 아래 내용을 이해합니다 | TextView | - | onboarding_agreement_text | 아래 내용을 이해합니다 | | 3 | 33 | 이 제품은 의료기기입니다 | TextView | - | onboarding_medical_device_title | 이 제품은 의료기기입니다 | | 4 | 33 | 이 애플리케이션은 의료기기입니다. 의료기기 라벨을 확인할 수 있습니다.. | TextView | - | onboarding_medical_device_description | 이 애플리케이션은 의료기기입니다. 의료기기 라벨을 확인할 수 있습니다.. | | 5 | 33 | 시작하기 | TextView | - | onboarding_done_text | 시작하기 | | 6 | 26 | onboarding_agreement_checkbox | CheckBox | - | onboarding_agreement_checkbox | - | | 7 | 21 | onboarding_done_button | View | - | onboarding_done_button | - | | 8 | 13 | 1 | TextView | - | - | 1 | | 9 | 13 | 사용자 연령은 20세 이상입니다 | TextView | - | - | 사용자 연령은 20세 이상입니다 | | 10 | 13 | 이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다. | TextView | - | - | 이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다. | | 11 | 13 | 2 | TextView | - | - | 2 | | 12 | 1 | (view: Button) | Button | - | - | - |
2. Selector Proposals
1. [수면무호흡 진단 보조앱 Apnotrack] (Score: 33)
- Accessibility ID:
~onboarding_title - XPath (Text):
//*[@text="수면무호흡 진단 보조앱 Apnotrack"] - XPath (Class+Text):
//android.widget.TextView[@text="수면무호흡 진단 보조앱 Apnotrack"]
2. [아래 내용을 이해합니다] (Score: 33)
- Accessibility ID:
~onboarding_agreement_text - XPath (Text):
//*[@text="아래 내용을 이해합니다"] - XPath (Class+Text):
//android.widget.TextView[@text="아래 내용을 이해합니다"]
3. [이 제품은 의료기기입니다] (Score: 33)
- Accessibility ID:
~onboarding_medical_device_title - XPath (Text):
//*[@text="이 제품은 의료기기입니다"] - XPath (Class+Text):
//android.widget.TextView[@text="이 제품은 의료기기입니다"]
4. [이 애플리케이션은 의료기기입니다. 의료기기 라벨을 확인할 수 있습니다..] (Score: 33)
- Accessibility ID:
~onboarding_medical_device_description - XPath (Text):
//*[@text="이 애플리케이션은 의료기기입니다. 의료기기 라벨을 확인할 수 있습니다.."] - XPath (Class+Text):
//android.widget.TextView[@text="이 애플리케이션은 의료기기입니다. 의료기기 라벨을 확인할 수 있습니다.."]
5. [시작하기] (Score: 33)
- Accessibility ID:
~onboarding_done_text - XPath (Text):
//*[@text="시작하기"] - XPath (Class+Text):
//android.widget.TextView[@text="시작하기"]
6. [onboarding_agreement_checkbox] (Score: 26)
- Accessibility ID:
~onboarding_agreement_checkbox
7. [onboarding_done_button] (Score: 21)
- Accessibility ID:
~onboarding_done_button
8. [1] (Score: 13)
- XPath (Text):
//*[@text="1"] - XPath (Class+Text):
//android.widget.TextView[@text="1"]
9. [사용자 연령은 20세 이상입니다] (Score: 13)
- XPath (Text):
//*[@text="사용자 연령은 20세 이상입니다"] - XPath (Class+Text):
//android.widget.TextView[@text="사용자 연령은 20세 이상입니다"]
10. [이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다.] (Score: 13)
- XPath (Text):
//*[@text="이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다."] - XPath (Class+Text):
//android.widget.TextView[@text="이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다."]
11. [2] (Score: 13)
- XPath (Text):
//*[@text="2"] - XPath (Class+Text):
//android.widget.TextView[@text="2"]
12. [(view: Button)] (Score: 1)
- XPath (Class):
//android.widget.Button
- Device: IOS 아이폰 12 | 18.7.2 18.7
- Scanned Elements: 12
- Generated: 11/28/2025, 3:04:06 PM
Note: ⚠️ indicates a shared ID (not unique).
1. Overview (Top Elements)
| # | Score | Context (Description) | Type | Name (ID) | Label | Value |
| --- | ----: | ---------------------------------------------------------------- | ---------- | ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 1 | 31 | uncheck | Image | onboarding_agreement_checkbox | uncheck | - |
| 2 | 31 | 아래 내용을 이해합니다 | StaticText | onboarding_agreement_text | 아래 내용을 이해합니다 | 아래 내용을 이해합니다 |
| 3 | 17 | 시작하기 | Button | onboarding_container ⚠️ | 시작하기 | - |
| 4 | 13 | 수면무호흡 진단 보조앱 | StaticText | onboarding_container ⚠️ | 수면무호흡 진단 보조앱 | 수면무호흡 진단 보조앱 |
| 5 | 13 | Apnotrack | StaticText | onboarding_container ⚠️ | Apnotrack | Apnotrack |
| 6 | 13 | 1 | StaticText | onboarding_container ⚠️ | 1 | 1 |
| 7 | 13 | 사용자 연령은 20세 이상입니다 | StaticText | onboarding_container ⚠️ | 사용자 연령은 20세 이상입니다 | 사용자 연령은 20세 이상입니다 |
| 8 | 13 | 이 제품은 20세 이상을 대상으로 사용되도록
되어 있습니다. | StaticText | onboarding_container ⚠️ | 이 제품은 20세 이상을 대상으로 사용되도록
되어 있습니다. | 이 제품은 20세 이상을 대상으로 사용되도록
되어 있습니다. |
| 9 | 13 | 2 | StaticText | onboarding_container ⚠️ | 2 | 2 |
| 10 | 13 | 이 제품은 의료기기입니다 | StaticText | onboarding_container ⚠️ | 이 제품은 의료기기입니다 | 이 제품은 의료기기입니다 |
| 11 | 13 | 이 애플리케이션은 의료기기 입니다. | StaticText | onboarding_container ⚠️ | 이 애플리케이션은 의료기기 입니다. | 이 애플리케이션은 의료기기 입니다. |
| 12 | 13 | 의료기기 라벨을 확인할 수 있습니다. | StaticText | onboarding_container ⚠️ | 의료기기 라벨을 확인할 수 있습니다. | 의료기기 라벨을 확인할 수 있습니다. |
2. Selector Proposals
1. [uncheck] (Score: 31)
- Accessibility ID:
~onboarding_agreement_checkbox - Class Chain (Label):
-ios class chain:**/XCUIElementTypeImage[label == "uncheck"] - Predicate (Label):
-ios predicate string:label == "uncheck" - XPath (Label):
//XCUIElementTypeImage[@label="uncheck"]
2. [아래 내용을 이해합니다] (Score: 31)
- Accessibility ID:
~onboarding_agreement_text - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "아래 내용을 이해합니다"] - Predicate (Label):
-ios predicate string:label == "아래 내용을 이해합니다" - XPath (Label):
//XCUIElementTypeStaticText[@label="아래 내용을 이해합니다"]
3. [시작하기] (Score: 17)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeButton[label == "시작하기"] - Predicate (Label):
-ios predicate string:label == "시작하기" - XPath (Label):
//XCUIElementTypeButton[@label="시작하기"]
4. [수면무호흡 진단 보조앱] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "수면무호흡 진단 보조앱"] - Predicate (Label):
-ios predicate string:label == "수면무호흡 진단 보조앱" - XPath (Label):
//XCUIElementTypeStaticText[@label="수면무호흡 진단 보조앱"]
5. [Apnotrack] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "Apnotrack"] - Predicate (Label):
-ios predicate string:label == "Apnotrack" - XPath (Label):
//XCUIElementTypeStaticText[@label="Apnotrack"]
6. [1] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "1"] - Predicate (Label):
-ios predicate string:label == "1" - XPath (Label):
//XCUIElementTypeStaticText[@label="1"]
7. [사용자 연령은 20세 이상입니다] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "사용자 연령은 20세 이상입니다"] - Predicate (Label):
-ios predicate string:label == "사용자 연령은 20세 이상입니다" - XPath (Label):
//XCUIElementTypeStaticText[@label="사용자 연령은 20세 이상입니다"]
8. [이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다.] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다."] - Predicate (Label):
-ios predicate string:label == "이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다." - XPath (Label):
//XCUIElementTypeStaticText[@label="이 제품은 20세 이상을 대상으로 사용되도록 되어 있습니다."]
9. [2] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "2"] - Predicate (Label):
-ios predicate string:label == "2" - XPath (Label):
//XCUIElementTypeStaticText[@label="2"]
10. [이 제품은 의료기기입니다] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "이 제품은 의료기기입니다"] - Predicate (Label):
-ios predicate string:label == "이 제품은 의료기기입니다" - XPath (Label):
//XCUIElementTypeStaticText[@label="이 제품은 의료기기입니다"]
11. [이 애플리케이션은 의료기기 입니다.] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "이 애플리케이션은 의료기기 입니다."] - Predicate (Label):
-ios predicate string:label == "이 애플리케이션은 의료기기 입니다." - XPath (Label):
//XCUIElementTypeStaticText[@label="이 애플리케이션은 의료기기 입니다."]
12. [의료기기 라벨을 확인할 수 있습니다.] (Score: 13)
- Accessibility ID:
~onboarding_container(Shared ID - caution) - Class Chain (Label):
-ios class chain:**/XCUIElementTypeStaticText[label == "의료기기 라벨을 확인할 수 있습니다."] - Predicate (Label):
-ios predicate string:label == "의료기기 라벨을 확인할 수 있습니다." - XPath (Label):
//XCUIElementTypeStaticText[@label="의료기기 라벨을 확인할 수 있습니다."]
Command Options
You can pass an options object to browser.dumpLocators() to customize its behavior.
| Option | Type | Default | Description |
| :--------------- | :------- | :------------------------------------------------------ | :----------------------------------------------------------------------------------- |
| outDir | string | './test/reports/locators' | Directory to save reports. |
| fileName | string | 'locator-report' | Prefix for the report file name. A timestamp is appended. |
| reportTitle | string | 'Platform-dependent ('... (Android)' or '... (iOS)')' | The main title inside the markdown report. If not provided, a default title is used. |
| limit | number | 60 | Maximum number of UI elements to include in the report. |
| stabilizeTries | number | 6 | How many times to check if the UI source has stabilized before analysis. |
Author
Jio (Lee Jiwon)
- Company: Asleep Inc.
- Role: Senior Software Quality Engineer (10+ years of experience)
- Contact: [email protected]
- Linkedin: linkedin.com/in/jiwon3027
License
MIT
