{"id":17545893,"url":"https://github.com/charlyautomatiza/boilerplate-webdriverio","last_synced_at":"2026-06-12T11:31:08.238Z","repository":{"id":115398505,"uuid":"574691595","full_name":"charlyautomatiza/boilerplate-webdriverio","owner":"charlyautomatiza","description":"Este repositorio contiene un boilerplate (plantilla inicial) para empezar a trabajar con WebdriverIO, un framework de automatización de pruebas para navegadores web y aplicaciones móviles.","archived":false,"fork":false,"pushed_at":"2026-04-02T21:40:36.000Z","size":1524,"stargazers_count":0,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-03T07:59:39.473Z","etag":null,"topics":["appium","appium-android","test-automation","testautomationframework","testing","testing-tools","webdriverio"],"latest_commit_sha":null,"homepage":"https://charlyautomatiza.tech/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/charlyautomatiza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-12-05T21:45:31.000Z","updated_at":"2026-04-02T21:40:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"f58e908b-81b5-4e87-92e3-d87ce929eaf3","html_url":"https://github.com/charlyautomatiza/boilerplate-webdriverio","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/charlyautomatiza/boilerplate-webdriverio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlyautomatiza%2Fboilerplate-webdriverio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlyautomatiza%2Fboilerplate-webdriverio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlyautomatiza%2Fboilerplate-webdriverio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlyautomatiza%2Fboilerplate-webdriverio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/charlyautomatiza","download_url":"https://codeload.github.com/charlyautomatiza/boilerplate-webdriverio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charlyautomatiza%2Fboilerplate-webdriverio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34243051,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appium","appium-android","test-automation","testautomationframework","testing","testing-tools","webdriverio"],"created_at":"2024-10-21T01:25:18.491Z","updated_at":"2026-06-12T11:31:08.232Z","avatar_url":"https://github.com/charlyautomatiza.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.youtube.com/c/CharlyAutomatiza?sub_confirmation=1\"\u003e\u003cimg src=\"https://img.shields.io/badge/CharlyAutomatiza-Youtube-FF0000.svg\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://discord.gg/wwM9GwxmRZ\"\u003e\u003cimg src=\"https://img.shields.io/badge/CharlyAutomatiza-Discord-5865F2.svg?style=flat\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"http://x.com/char_automatiza\"\u003e\u003cimg src=\"https://img.shields.io/badge/@char__automatiza-X-000000.svg?style=flat\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.linkedin.com/in/gautocarlos/\"\u003e\u003cimg src=\"https://img.shields.io/badge/Carlos%20 Gauto-LinkedIn-0077B5.svg\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://charlyautomatiza.tech\"\u003e\u003cimg src=\"https://img.shields.io/badge/Website-charlyautomatiza.tech-4285F4.svg?style=flat\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://dev.to/charlyautomatiza\"\u003e\u003cimg src=\"https://img.shields.io/badge/CharlyAutomatiza-DEV.to-0A0A0A.svg?style=flat\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.instagram.com/charlyautomatiza/\"\u003e\u003cimg alt=\"Instagram\" src=\"https://img.shields.io/badge/CharlyAutomatiza-Instagram-E4405F.svg?style=flat\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.twitch.tv/charlyautomatiza\"\u003e\u003cimg alt=\"Twitch\" src=\"https://img.shields.io/badge/CharlyAutomatiza-Twitch-9146FF.svg\" style=\"max-height: 300px;\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://webdriver.io/\"\u003e\n        \u003cimg alt=\"WebdriverIO\" src=\"https://webdriver.io/assets/images/robot-3677788dd63849c56aa5cb3f332b12d5.svg\" width=\"146\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n# WebdriverIO Boilerplate\n\nThis repository contains a starter boilerplate for working with [WebdriverIO](https://webdriver.io/), a test automation framework for web browsers and mobile applications.\n\n## Prerequisites\n\nMake sure you have Node.js installed. Download it from [nodejs.org](https://nodejs.org/).\n\nRecommended setup resources:\n\n* Blog guide: [Appium v2 Android Setup Guide](https://bit.ly/appium-v2-android-setup)\n* YouTube video: [Appium v2 - WebdriverIO](https://bit.ly/3UEQbHt) (some CLI options may have changed since recording)\n\n## Installation\n\n1. Clone this repository:\n\n```bash\ngit clone https://github.com/charlyautomatiza/boilerplate-webdriverio.git\n```\n\n2. Change into the project directory:\n\n```bash\ncd boilerplate-webdriverio\n```\n\n3. Install dependencies:\n\n```bash\nnpm install\n```\n\n## Running Tests\n\nDownload the latest release of the [WebdriverIO Native Demo (Guinea Pig) App](https://github.com/webdriverio/native-demo-app/releases) for Android (and iOS if needed).\n\nCreate a folder named `app` at the project root and place the APK there to avoid path issues.\n\nAfter configuring your environment variables (ANDROID_HOME / Java etc.), run the tests:\n\n```bash\nnpm run wdio\n```\n\nThis command runs the E2E tests and produces Allure + JUnit reports.\n\n## Open the Combined Allure Report\n\n```bash\nnpm run open-report\n```\n\n## Continuous Integration: Android Emulator (GitHub Actions)\n\nThe included GitHub Actions workflow automatically runs the E2E tests on an Android emulator. It:\n\n- Downloads the demo APK into `app/`\n- Grants required emulator permissions\n- Boots an Android emulator (API 34 / Android 14) via `reactivecircus/android-emulator-runner`\n- Installs dependencies (Node.js, Appium, WebdriverIO, etc.)\n- Executes the E2E tests\n- Publishes Allure and JUnit results as artifacts\n\nTriggers: on every push or pull request to `main`.\n\nSee `.github/workflows/android-emulator.yml` for full details.\n\n## Advanced Examples in `test.e2e.ts`\n\nThis project showcases advanced testing patterns (order reflects appearance in the spec file):\n\n### 1. Basic Login Test\nValidates the core happy path (sanity check for environment, selectors, and app readiness).\n\n```typescript\nit('should login with valid credentials', async () =\u003e {\n    await LoginPage.loginBtn.click();\n    await LoginPage.login('tomsmith@mail.com', 'SuperSecretPassword!');\n    await expect(AlertPage.messageAlert).toHaveText(expect.stringContaining('You are logged in!'));\n});\n```\n\n### 2. AAA Pattern (Arrange-Act-Assert)\nClarifies test intent by separating setup, action, and verification.\n\n```typescript\nit('should login successfully following the AAA pattern', async () =\u003e {\n    // Arrange\n    await LoginPage.loginBtn.click();\n    await expect(LoginPage.inputUsername).toBeDisplayed();\n    await expect(LoginPage.inputPassword).toBeDisplayed();\n    await expect(LoginPage.btnSubmit).toBeDisplayed();\n    // Act\n    await LoginPage.login('tomsmith@mail.com', 'SuperSecretPassword!');\n    // Assert\n    await expect(AlertPage.messageAlert).toHaveText(\n        expect.stringContaining('You are logged in!'),\n        { message: 'Confirmation message is not the expected one' }\n    );\n});\n```\n\n### 3. Data-Driven Testing (DDT) with JSON\nEach JSON row generates its own test case using a `for...of` loop for granular reporting.\n\n```typescript\n// Load JSON data once\nconst jsonData: LoginData[] = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n\nfor (const row of jsonData) {\n    it(`DDT JSON - User: \"${row.username}\" → message: \"${row.expectedMessage}\"`, async () =\u003e {\n        // Arrange\n        await LoginPage.loginBtn.click();\n        // Act\n        await LoginPage.login(row.username, row.password);\n        // Assert (implicit wait via expect)\n        await expect(AlertPage.messageAlert).toHaveText(\n            expect.stringContaining(row.expectedMessage),\n            { message: `Expected message: \"${row.expectedMessage}\" for user: ${row.username}` }\n        );\n    });\n}\n```\n\n**Data file:** `test/data/loginData.json`\n```json\n[\n  { \"username\": \"tomsmith@mail.com\", \"password\": \"SuperSecretPassword!\", \"expectedMessage\": \"You are logged in!\" },\n  { \"username\": \"user@test.com\", \"password\": \"TestPass123!\", \"expectedMessage\": \"You are logged in!\" },\n  { \"username\": \"admin@example.com\", \"password\": \"Admin2024!\", \"expectedMessage\": \"You are logged in!\" }\n]\n```\n\n### 4. Data-Driven Testing (DDT) with CSV\nCSV parsing transforms raw text into structured objects. Each row becomes an independent test for clearer failure isolation.\n\n```typescript\n// Load and parse CSV at runtime\nconst csvContent = fs.readFileSync(filePath, 'utf-8');\nconst lines = csvContent.trim().split('\\n');\n\nconst userData: UserData[] = lines.slice(1).map(line =\u003e {\n    const values = line.split(',').map(v =\u003e v.replace(/\"/g, '').trim());\n    return {\n        username: values[0],\n        password: values[1],\n        expectedAction: values[2]\n    };\n});\n\nfor (const row of userData) {\n    it(`DDT CSV - User: \"${row.username}\" → expected action: ${row.expectedAction}` , async () =\u003e {\n        // Arrange\n        await LoginPage.loginBtn.click();\n        // Act\n        await LoginPage.login(row.username, row.password);\n        // Assert\n        await expect(AlertPage.messageAlert).toHaveText(\n            expect.stringContaining(row.expectedAction)\n        );\n    });\n}\n```\n\n**Data file:** `test/data/buttonsData.csv`\n```csv\nusername,password,expectedAction\nuser1@test.com,Pass123!,login_success\nuser2@test.com,Pass456!,login_success\n```\n\n### (Moved Above) AAA Pattern (Arrange-Act-Assert)\nAlready documented as example 2 to reflect execution order.\n\n```typescript\nit('should login successfully following the AAA pattern', async () =\u003e {\n    // Arrange\n    await LoginPage.loginBtn.click();\n    await expect(LoginPage.inputUsername).toBeDisplayed();\n    await expect(LoginPage.inputPassword).toBeDisplayed();\n    await expect(LoginPage.btnSubmit).toBeDisplayed();\n    // Act\n    await LoginPage.login('tomsmith@mail.com', 'SuperSecretPassword!');\n    // Assert\n    await expect(AlertPage.messageAlert).toHaveText(\n        expect.stringContaining('You are logged in!'),\n        { message: 'Confirmation message is not the expected one' }\n    );\n});\n```\n\n### 4. Good Practices Implemented\n\n#### a. Granular DDT via `for...of`\nAvoid `forEach` for async tests—`for...of` preserves proper async flow and allows dynamic test titles.\n\n#### b. Implicit waits through `expect`\n`expect(...).toHaveText()` inherently waits for the condition, reducing flakiness versus fixed sleeps.\n\n#### c. Clear assertion messages\nCustom messages help pinpoint data row failures quickly.\n\n#### d. Page Object Model (POM)\nAll app interactions go through `LoginPage` and `AlertPage`, centralizing selectors and actions.\n\n```typescript\n// test/pageobjects/login.page.ts\nclass LoginPage {\n    public get loginBtn () {\n        return $('~Login');\n    }\n\n    public get inputUsername () {\n        return $('~input-email');\n    }\n\n    public get inputPassword () {\n        return $('~input-password');\n    }\n\n    public async login (username: string, password: string) {\n        await this.inputUsername.setValue(username);\n        await this.inputPassword.setValue(password);\n        await this.btnSubmit.click();\n    }\n}\n```\n\n### File Structure\n\n```\ntest/\n├── data/\n│   ├── loginData.json       # Data file for JSON-driven tests\n│   └── buttonsData.csv      # Data file for CSV-driven tests\n├── pageobjects/\n│   ├── login.page.ts        # Login Page Object\n│   └── alert.page.ts        # Alert Page Object\n├── types/\n│   └── data.ts              # Shared TypeScript interfaces (LoginData, UserData)\n└── specs/\n    └── test.e2e.ts          # E2E tests (basic, AAA, JSON DDT, CSV DDT)\n```\n\n### Execution \u0026 Reporting Summary\n\nCurrent suite produces (in execution order):\n1. 1 basic login test\n2. 1 AAA pattern test\n3. 3 JSON DDT test cases\n4. 2 CSV DDT test cases\n\nAllure \u0026 JUnit reports show distinct titles per dataset row for clarity.\n\n### Rationale per Example\n\n1. Basic Login: Verifies essential flow and environment stability before deeper scenarios.\n2. AAA Pattern: Reinforces explicit structure for clarity and future maintenance.\n3. JSON DDT: Scales rapidly—add rows to expand coverage without modifying test logic.\n4. CSV DDT: Demonstrates ingestion of spreadsheet-style data for flexible scenario expansion.\n\n### Recommended Improvements (Next Steps)\n1. Extract interfaces (`LoginData`, `UserData`) outside the `describe` for reuse.\n2. Add negative login scenarios (invalid password / empty fields) to demonstrate error handling.\n3. Add screenshot / log attachment steps on failure integrated into Allure.\n4. Parameterize device capabilities for multi-platform matrix (Android/iOS).\n5. Introduce test tagging to selectively run subsets (e.g., smoke vs regression).\n\n### Mobile Test Automation Anti-Patterns to Avoid\n\n| Anti-Pattern | Why It Hurts | Better Approach |\n|--------------|-------------|-----------------|\n| Hard-coded sleeps (e.g. `browser.pause(3000)`) | Brittle \u0026 slow; ignores real UI readiness | Implicit waits via WebdriverIO expectations (`toBeDisplayed`, `toHaveText`) |\n| Using `forEach` for async test generation | Does not await properly; can create false positives | Use `for...of` to generate discrete `it` blocks |\n| Mixing test logic \u0026 selectors inline | Duplicates selectors; hard to refactor | Page Object Model centralizes selectors/actions |\n| Long monolithic test cases | Hard to debug; single failure hides others | Split by scenario/data; keep tests focused |\n| Global mutable state between tests | Causes leakage \u0026 flaky order-dependent results | Reset app/session in hooks (`afterTest`) |\n| Overuse of generic XPath selectors | Fragile when UI shifts; performance overhead | Prefer accessibility IDs / resource IDs |\n| Ignoring platform differences | Unexpected failures on iOS vs Android | Abstract platform-specific selectors/methods |\n| Silent assertions (no message) | Harder triage in large suites | Provide assertion messages with contextual data |\n| No reporting attachments | Limited diagnostics for CI failures | Capture screenshots/logcat on failure |\n| Testing multiple flows in one test | One failure invalidates many checks | Keep single, clear purpose per test |\n\n### Rationale for Restart Strategy\nEnsuring a clean app state after each test prevents leakage (e.g., leftover logged-in session) that can hide defects. The current `afterTest` hook invokes `browser.relaunchActiveApp()`, offering a lightweight refresh versus a full session recreation, reducing execution time while still clearing in-app UI state.\n\n### Design Principles Emphasized\n- Deterministic tests (no arbitrary waits)\n- Readable structure (AAA, clear titles)\n- Data externalization for scalability\n- Fast failure diagnosis (granular test cases + messages)\n- Maintainability (POM encapsulation)\n\n### Why Shared Types Matter\nUsing centralized TypeScript interfaces (`LoginData`, `UserData` in `test/types/data.ts`) provides several concrete benefits:\n1. Single Source of Truth: Changing a field (e.g. renaming `expectedMessage`) updates all specs and utilities at compile time—no silent drift.\n2. Early Feedback: Type errors surface during development instead of causing runtime `undefined` or assertion mismatches in CI.\n3. Safer Refactors: IDE auto-complete and rename tools work reliably across the suite, reducing regressions in large data-driven expansions.\n4. Data Contract Clarity: New contributors immediately see required fields and their intent, lowering onboarding friction.\n5. Prevents “Magic” Fields: Explicit interfaces discourage ad‑hoc additions to JSON/CSV that tests forget to assert.\n6. Enables Reuse: Utilities (parsers, generators, factories) can accept typed objects, improving composability and test scaffolding.\n7. Facilitates Lint \u0026 Static Analysis: Linters and quality tools (e.g. SonarQube) reason better about well-defined shapes versus dynamic objects.\n\nAnti‑Pattern Avoided: Defining interfaces inline inside each `describe` causes duplication and accidental divergence; centralizing them avoids this drift.\n\n## Contributing\n\nFeel free to open issues or submit pull requests for enhancements and fixes.\n\n## Support\n\nNeed help? Open an issue. More info: [WebdriverIO Docs](https://webdriver.io/docs/gettingstarted.html).\n\nAdditional content \u0026 updates: [Website](https://charlyautomatiza.tech) • Community Discord: [Join here](https://bit.ly/charlyAutomatiza-discord).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlyautomatiza%2Fboilerplate-webdriverio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcharlyautomatiza%2Fboilerplate-webdriverio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharlyautomatiza%2Fboilerplate-webdriverio/lists"}