Selenium WebDriver is the industry-standard open-source framework for browser automation and web application testing. First released in 2004, it remains widely used in enterprise environments despite newer alternatives like Playwright, particularly for legacy system testing.
Selenium for:
├── Legacy insurance systems (20 years old)
├── Existing test suites (5,000+ tests)
├── Java-based applications
├── Selenium Grid infrastructure (already invested)
└── Team expertise (10+ years)
Playwright for:
├── New Cursor integrations
├── Modern web apps
├── Azure portal testing
├── API testing
└── Faster test development
Why Not Migrate?
- 5,000 Selenium tests = 6 months to rewrite
- Cost: $500K (labor + opportunity cost)
- Benefit: Marginal (existing tests work fine)
- Risk: Regression issues during migration
- Decision: Keep Selenium for legacy, add Playwright for new tests
Selenium Grid Architecture
Distributed Testing Infrastructure
graph TB
subgraph "Test Execution"
A[Azure DevOps Agent]
B[Test Scripts - Java]
end
subgraph "Selenium Grid Hub"
C[Hub - Session Manager]
D[Session Queue]
end
subgraph "Browser Nodes - Windows"
E[Chrome Node 1]
F[Chrome Node 2]
G[Edge Node]
end
subgraph "Browser Nodes - Linux"
H[Firefox Node]
I[Chrome Node 3]
end
A --> B
B --> C
C --> D
D --> E
D --> F
D --> G
D --> H
D --> I
style C fill:#10b981,stroke:#059669,color:#fff
style D fill:#f59e0b,stroke:#d97706,color:#fff
# docker-compose.yml for Selenium Gridversion:'3.8'services:selenium-hub:image:selenium/hub:4.15.0ports:-"4444:4444"-"4442:4442"-"4443:4443"environment:-GRID_MAX_SESSION=10-GRID_BROWSER_TIMEOUT=300-GRID_TIMEOUT=300networks:-selenium-gridchrome-node:image:selenium/node-chrome:4.15.0depends_on:-selenium-hubenvironment:-SE_EVENT_BUS_HOST=selenium-hub-SE_EVENT_BUS_PUBLISH_PORT=4442-SE_EVENT_BUS_SUBSCRIBE_PORT=4443-SE_NODE_MAX_SESSIONS=5shm_size:2gbnetworks:-selenium-griddeploy:replicas:3firefox-node:image:selenium/node-firefox:4.15.0depends_on:-selenium-hubenvironment:-SE_EVENT_BUS_HOST=selenium-hub-SE_EVENT_BUS_PUBLISH_PORT=4442-SE_EVENT_BUS_SUBSCRIBE_PORT=4443-SE_NODE_MAX_SESSIONS=5shm_size:2gbnetworks:-selenium-griddeploy:replicas:2edge-node:image:selenium/node-edge:4.15.0depends_on:-selenium-hubenvironment:-SE_EVENT_BUS_HOST=selenium-hub-SE_EVENT_BUS_PUBLISH_PORT=4442-SE_EVENT_BUS_SUBSCRIBE_PORT=4443-SE_NODE_MAX_SESSIONS=5shm_size:2gbnetworks:-selenium-gridnetworks:selenium-grid:driver:bridge
packagecom.company.security.tests;importorg.junit.jupiter.api.Test;importorg.openqa.selenium.By;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.WebElement;importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.support.ui.ExpectedConditions;importorg.openqa.selenium.support.ui.WebDriverWait;importjava.time.Duration;importstaticorg.junit.jupiter.api.Assertions.*;publicclassKeyVaultSecurityTest{@TestpublicvoidtestKeyVaultRequiresMFA(){WebDriverdriver=newChromeDriver();try{// Navigate to Azure Portaldriver.get("https://portal.azure.com");// Login with test accountWebElementemailInput=driver.findElement(By.id("i0116"));emailInput.sendKeys(System.getenv("TEST_USER"));driver.findElement(By.id("idSIButton9")).click();// Enter passwordWebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(10));WebElementpasswordInput=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("i0118")));passwordInput.sendKeys(System.getenv("TEST_PASSWORD"));driver.findElement(By.id("idSIButton9")).click();// MFA challenge should appearWebElementmfaPrompt=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("idDiv_SAOTCAS_Title")));assertTrue(mfaPrompt.isDisplayed(),"MFA prompt should be displayed");StringmfaText=mfaPrompt.getText();assertTrue(mfaText.contains("Verify")||mfaText.contains("Authenticate"),"MFA verification prompt should be shown");System.out.println("✅ MFA enforcement verified for Key Vault access");}finally{driver.quit();}}@TestpublicvoidtestDeveloperCannotAccessProductionSecrets(){WebDriverdriver=newChromeDriver();try{// Login as developerloginAsUser(driver,"developer@company.com","DevPassword123!");// Navigate to production Key Vaultdriver.get("https://portal.azure.com/#@company.com/resource/subscriptions/xxx/resourceGroups/prod-rg/providers/Microsoft.KeyVault/vaults/keyvault-prod-001/secrets");WebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(15));// Should see access denied messageWebElementaccessDenied=wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[contains(text(), 'Access denied') or contains(text(), 'You do not have permission')]")));assertTrue(accessDenied.isDisplayed(),"Access should be denied for developers");// Verify no secrets are visiblebooleansecretsVisible=driver.findElements(By.cssSelector(".secret-list-item")).size()>0;assertFalse(secretsVisible,"Developer should not see any secrets");System.out.println("✅ RBAC properly blocks developer from production secrets");}finally{driver.quit();}}@TestpublicvoidtestSecurityAdminCanAccessProductionSecrets(){WebDriverdriver=newChromeDriver();try{// Login as security adminloginAsUser(driver,"secadmin@company.com","SecAdminPassword123!");// Navigate to production Key Vaultdriver.get("https://portal.azure.com/#@company.com/resource/subscriptions/xxx/resourceGroups/prod-rg/providers/Microsoft.KeyVault/vaults/keyvault-prod-001/secrets");WebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(15));// Should see secret listWebElementsecretList=wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".secret-list")));assertTrue(secretList.isDisplayed(),"Security admin should see secret list");// Count accessible secretsintsecretCount=driver.findElements(By.cssSelector(".secret-list-item")).size();assertTrue(secretCount>0,"Security admin should see secrets");System.out.println("✅ Security admin has proper access to "+secretCount+" secrets");// Log access for auditlogSecurityAccess(driver,"secadmin@company.com","keyvault-prod-001",secretCount);}finally{driver.quit();}}privatevoidloginAsUser(WebDriverdriver,Stringusername,Stringpassword){driver.get("https://portal.azure.com");WebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(10));// Enter usernameWebElementemailInput=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("i0116")));emailInput.sendKeys(username);driver.findElement(By.id("idSIButton9")).click();// Enter passwordWebElementpasswordInput=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("i0118")));passwordInput.sendKeys(password);driver.findElement(By.id("idSIButton9")).click();// Handle MFA (in test environment, auto-approved)try{WebElementmfaApprove=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("idBtn_Back")));mfaApprove.click();}catch(Exceptione){// MFA may auto-approve in test environment}// Wait for portal to loadwait.until(ExpectedConditions.urlContains("portal.azure.com"));}privatevoidlogSecurityAccess(WebDriverdriver,Stringuser,Stringresource,intitemCount){// Log to SIEM via APISystem.out.println("AUDIT: User "+user+" accessed "+resource+" with "+itemCount+" items visible");}}
publicclassOktaSSOSecurityTest{@TestpublicvoidtestOktaSSOMFAEnforcement(){WebDriverdriver=newChromeDriver();try{// Navigate to Cursor enterprise portaldriver.get("https://cursor.company.com");WebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(10));// Click SSO loginWebElementssoButton=wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[contains(text(), 'Sign in with SSO')]")));ssoButton.click();// Enter company domainWebElementdomainInput=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("domain")));domainInput.sendKeys("company.com");driver.findElement(By.id("next")).click();// Should redirect to Oktawait.until(ExpectedConditions.urlContains("okta.com"));assertTrue(driver.getCurrentUrl().contains("okta.com"),"Should redirect to Okta");// Enter credentialsWebElementusername=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("okta-signin-username")));username.sendKeys(System.getenv("TEST_USER"));WebElementpassword=driver.findElement(By.id("okta-signin-password"));password.sendKeys(System.getenv("TEST_PASSWORD"));driver.findElement(By.id("okta-signin-submit")).click();// MFA challenge must appearWebElementmfaChallenge=wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[contains(text(), 'Verify') or contains(text(), 'Multi-Factor')]")));assertTrue(mfaChallenge.isDisplayed(),"MFA challenge must be presented");System.out.println("✅ Okta SSO properly enforces MFA");}finally{driver.quit();}}@TestpublicvoidtestCannotBypassOktaMFA(){WebDriverdriver=newChromeDriver();try{// Attempt to access dashboard directlydriver.get("https://cursor.company.com/dashboard");WebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(10));// Should redirect to Okta loginwait.until(ExpectedConditions.urlContains("okta.com"));assertTrue(driver.getCurrentUrl().contains("okta.com"),"Should redirect to Okta login");// Attempt cookie manipulationdriver.manage().addCookie(neworg.openqa.selenium.Cookie("session","fake-session-token-12345"));driver.manage().addCookie(neworg.openqa.selenium.Cookie("auth","bypassed-auth-token"));// Try to access dashboard againdriver.get("https://cursor.company.com/dashboard");// Should still redirect to Okta (MFA required)wait.until(ExpectedConditions.urlContains("okta.com"));assertTrue(driver.getCurrentUrl().contains("okta.com"),"Cookie manipulation should not bypass Okta MFA");System.out.println("✅ Cannot bypass Okta MFA with cookie manipulation");}finally{driver.quit();}}}
publicclassDLPSecurityTest{@TestpublicvoidtestPurviewBlocksAPIKeyInTeams(){WebDriverdriver=newChromeDriver();try{// Login to TeamsloginToTeams(driver);WebDriverWaitwait=newWebDriverWait(driver,Duration.ofSeconds(10));// Navigate to test channeldriver.get("https://teams.microsoft.com/_#/conversations/Test%20Channel");// Try to send Azure OpenAI API keyStringfakeApiKey="sk-proj-test1234567890abcdef1234567890abcdef1234567890";WebElementmessageInput=wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("[data-tid='messageComposerInput']")));messageInput.sendKeys("Here's the API key: "+fakeApiKey);WebElementsendButton=driver.findElement(By.cssSelector("[data-tid='send-message']"));sendButton.click();// Should see DLP block messageWebElementdlpBlock=wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[contains(text(), 'blocked by policy') or contains(text(), 'DLP')]")));assertTrue(dlpBlock.isDisplayed(),"Purview DLP should block API key");// Verify message was not sentbooleanmessageExists=driver.findElements(By.xpath("//*[contains(text(), '"+fakeApiKey+"')]")).stream().anyMatch(e->e.isDisplayed());assertFalse(messageExists,"API key should not appear in chat history");System.out.println("✅ Purview DLP successfully blocked API key in Teams");}finally{driver.quit();}}privatevoidloginToTeams(WebDriverdriver){driver.get("https://teams.microsoft.com");// Login implementation...}}
@AfterEachpublicvoidcleanup(){if(driver!=null){// Logouttry{driver.get("https://cursor.company.com/logout");}catch(Exceptione){// Ignore}// Clear cookiesdriver.manage().deleteAllCookies();// Close browserdriver.quit();}}
4. Parallel Execution
Leverage Selenium Grid for speed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Execution(ExecutionMode.CONCURRENT)publicclassParallelSecurityTests{@TestpublicvoidtestOktaMFA(){// Test implementation}@TestpublicvoidtestAzureFirewall(){// Test implementation}// Both tests run in parallel on different Grid nodes}
// LoginPage.javapublicclassLoginPage{privateWebDriverdriver;privateWebDriverWaitwait;@FindBy(id="username")privateWebElementusernameInput;@FindBy(id="password")privateWebElementpasswordInput;@FindBy(id="login-button")privateWebElementloginButton;@FindBy(xpath="//*[contains(text(), 'MFA') or contains(text(), 'Multi-Factor')]")privateWebElementmfaPrompt;publicLoginPage(WebDriverdriver){this.driver=driver;this.wait=newWebDriverWait(driver,Duration.ofSeconds(10));PageFactory.initElements(driver,this);}publicvoidlogin(Stringusername,Stringpassword){usernameInput.sendKeys(username);passwordInput.sendKeys(password);loginButton.click();}publicbooleanisMFAPromptDisplayed(){try{wait.until(ExpectedConditions.visibilityOf(mfaPrompt));returnmfaPrompt.isDisplayed();}catch(TimeoutExceptione){returnfalse;}}}// Usage in tests@TestpublicvoidtestMFAEnforcement(){driver.get("https://cursor.company.com");LoginPageloginPage=newLoginPage(driver);loginPage.login("testuser@company.com","TestPassword123!");assertTrue(loginPage.isMFAPromptDisplayed(),"MFA should be enforced");}
publicclassSecurityTestListenerimplementsTestWatcher{@OverridepublicvoidtestFailed(ExtensionContextcontext,Throwablecause){StringtestName=context.getDisplayName();// Take screenshotWebDriverdriver=getDriverFromContext(context);Filescreenshot=((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);// Log to fileSystem.err.println("❌ Security test failed: "+testName);System.err.println("Error: "+cause.getMessage());// Alert security teamalertSecurityTeam(testName,cause.getMessage(),screenshot);// Log to Chronicle SIEMlogToSIEM(testName,"failed",cause.getMessage());}@OverridepublicvoidtestSuccessful(ExtensionContextcontext){StringtestName=context.getDisplayName();System.out.println("✅ Security test passed: "+testName);// Log to Chronicle SIEMlogToSIEM(testName,"passed",null);}privatevoidalertSecurityTeam(Stringtest,Stringerror,Filescreenshot){// Send to Teams webhook// Implementation details...}privatevoidlogToSIEM(Stringtest,Stringstatus,Stringerror){// Send to Chronicle via EventHub// Implementation details...}}