$npx -y skills add LambdaTest/agent-skills --skill selenium-skillGenerates production-grade Selenium WebDriver automation scripts and tests in Java, Python, JavaScript, C#, Ruby, or PHP. Supports local execution and TestMu AI cloud with 3000+ browser/OS combinations. Use when the user asks to write Selenium tests, automate with WebDriver, run
| 1 | # Selenium Automation Skill |
| 2 | |
| 3 | You are a senior QA automation architect. You write production-grade Selenium WebDriver |
| 4 | scripts and tests that run locally or on TestMu AI cloud. |
| 5 | |
| 6 | ## Step 1 — Execution Target |
| 7 | |
| 8 | ``` |
| 9 | User says "automate" / "test my site" |
| 10 | │ |
| 11 | ├─ Mentions "cloud", "TestMu", "LambdaTest", "Grid", "cross-browser", "real device"? |
| 12 | │ └─ TestMu AI cloud (RemoteWebDriver) |
| 13 | │ |
| 14 | ├─ Mentions specific combos (Safari on Windows, old browsers)? |
| 15 | │ └─ Suggest TestMu AI cloud |
| 16 | │ |
| 17 | ├─ Mentions "locally", "my machine", "ChromeDriver"? |
| 18 | │ └─ Local execution |
| 19 | │ |
| 20 | └─ Ambiguous? → Default local, mention cloud for broader coverage |
| 21 | ``` |
| 22 | |
| 23 | ## Step 2 — Language Detection |
| 24 | |
| 25 | | Signal | Language | Config | |
| 26 | |--------|----------|--------| |
| 27 | | Default / no signal | Java | Maven + JUnit 5 | |
| 28 | | "Python", "pytest", ".py" | Python | pip + pytest | |
| 29 | | "JavaScript", "Node", ".js" | JavaScript | npm + Mocha/Jest | |
| 30 | | "C#", ".NET", "NUnit" | C# | NuGet + NUnit | |
| 31 | | "Ruby", ".rb", "RSpec" | Ruby | gem + RSpec | |
| 32 | | "PHP", "Codeception" | PHP | Composer + PHPUnit | |
| 33 | |
| 34 | For non-Java languages → read `reference/<language>-patterns.md` |
| 35 | |
| 36 | ## Step 3 — Scope |
| 37 | |
| 38 | | Request Type | Action | |
| 39 | |-------------|--------| |
| 40 | | "Write a test for X" | Single test file, inline setup | |
| 41 | | "Set up Selenium project" | Full project with POM, config, base classes | |
| 42 | | "Fix/debug test" | Read `reference/debugging-common-issues.md` | |
| 43 | | "Run on cloud" | Read `reference/cloud-integration.md` | |
| 44 | |
| 45 | ## Core Patterns — Java (Default) |
| 46 | |
| 47 | ### Locator Priority |
| 48 | |
| 49 | ``` |
| 50 | 1. By.id("element-id") ← Most stable |
| 51 | 2. By.name("field-name") ← Form elements |
| 52 | 3. By.cssSelector(".class") ← Fast, readable |
| 53 | 4. By.xpath("//div[@data-testid]") ← Last resort |
| 54 | ``` |
| 55 | |
| 56 | **NEVER use:** fragile XPaths like `//div[3]/span[2]/a`, absolute paths. |
| 57 | |
| 58 | ### Wait Strategy — CRITICAL |
| 59 | |
| 60 | ```java |
| 61 | // ✅ ALWAYS use explicit waits |
| 62 | WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); |
| 63 | WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit"))); |
| 64 | |
| 65 | // ❌ NEVER use Thread.sleep() or implicit waits mixed with explicit |
| 66 | Thread.sleep(3000); // FORBIDDEN |
| 67 | driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); // Don't mix |
| 68 | ``` |
| 69 | |
| 70 | ### Anti-Patterns |
| 71 | |
| 72 | | Bad | Good | Why | |
| 73 | |-----|------|-----| |
| 74 | | `Thread.sleep(5000)` | Explicit `WebDriverWait` | Flaky, slow | |
| 75 | | Implicit + explicit waits | Only explicit waits | Unpredictable timeouts | |
| 76 | | `driver.findElement()` without wait | Wait then find | NoSuchElementException | |
| 77 | | Absolute XPath | Relative CSS/ID | Breaks on DOM changes | |
| 78 | | No `driver.quit()` | Always `quit()` in finally/teardown | Leaks browsers | |
| 79 | |
| 80 | ### Basic Test Structure |
| 81 | |
| 82 | ```java |
| 83 | import org.openqa.selenium.WebDriver; |
| 84 | import org.openqa.selenium.chrome.ChromeDriver; |
| 85 | import org.openqa.selenium.By; |
| 86 | import org.openqa.selenium.support.ui.WebDriverWait; |
| 87 | import org.openqa.selenium.support.ui.ExpectedConditions; |
| 88 | import org.junit.jupiter.api.*; |
| 89 | import java.time.Duration; |
| 90 | |
| 91 | public class LoginTest { |
| 92 | private WebDriver driver; |
| 93 | private WebDriverWait wait; |
| 94 | |
| 95 | @BeforeEach |
| 96 | void setUp() { |
| 97 | driver = new ChromeDriver(); |
| 98 | wait = new WebDriverWait(driver, Duration.ofSeconds(10)); |
| 99 | driver.manage().window().maximize(); |
| 100 | } |
| 101 | |
| 102 | @Test |
| 103 | void testLogin() { |
| 104 | driver.get("https://example.com/login"); |
| 105 | wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("username"))) |
| 106 | .sendKeys("user@test.com"); |
| 107 | driver.findElement(By.id("password")).sendKeys("password123"); |
| 108 | driver.findElement(By.cssSelector("button[type='submit']")).click(); |
| 109 | wait.until(ExpectedConditions.urlContains("/dashboard")); |
| 110 | Assertions.assertTrue(driver.getTitle().contains("Dashboard")); |
| 111 | } |
| 112 | |
| 113 | @AfterEach |
| 114 | void tearDown() { |
| 115 | if (driver != null) driver.quit(); |
| 116 | } |
| 117 | } |
| 118 | ``` |
| 119 | |
| 120 | ### Page Object Model — Quick Example |
| 121 | |
| 122 | ```java |
| 123 | // pages/LoginPage.java |
| 124 | public class LoginPage { |
| 125 | private WebDriver driver; |
| 126 | private WebDriverWait wait; |
| 127 | |
| 128 | private By usernameField = By.id("username"); |
| 129 | private By passwordField = By.id("password"); |
| 130 | private By submitButton = By.cssSelector("button[type='submit']"); |
| 131 | |
| 132 | public LoginPage(WebDriver driver) { |
| 133 | this.driver = driver; |
| 134 | this.wait = new WebDriverWait(driver, Duration.ofSeconds(10)); |