@techgirl1908

❝Existing Selenium projects will continue for a long time, but after learning Playwright, I will never start a new E2E test project in Selenium.❞ ~ Andrejs

VS

@AutomationPanda

@techgirl1908

❝I have used both for some time now and can’t wait to see what points both speakers put.❞ ~ Rahul

VS

@AutomationPanda

@techgirl1908

❝I have used both selenium and playwright(js binding). For me playwright is the clear winner. Let the code speak❞ ~ Dushyant

VS

@AutomationPanda

@techgirl1908

@AutomationPanda

❝About time Selenium got a worthy competitor. I've been a WebDriver fan for good 7+ years and can’t wait to see how long will Playwright survive against it 😉❞ ~ Akash

VS

@techgirl1908

@AutomationPanda

❝Sorry Angie. On this one I’m team Andy, my bet is on Playwright❞ ~ Ixchel

VS

@techgirl1908

@AutomationPanda

❝Playwright is absolutely changing the game! Automatic waits, built in device and multiple browser support, available in Python, JS, and more! ❞ ~ Sean

VS

Why compare

Selenium vs Playwright?

@techgirl1908

@AutomationPanda

👩🏽‍💻Selenium WebDriver (Java)

 

🧑🏻‍💻Playwright

(Java)

@techgirl1908

@AutomationPanda

The Battle

@techgirl1908

@AutomationPanda

🥊 10 Rounds
 

💻 Show implementations in Selenium and Playwright
 

🗳 You vote
 

💬 Discuss after each round

Round 1:

Element Interaction

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

Round 1: Playwright

@Test
public void testLogin()
{
    page.navigate("https://demo.applitools.com/");
    page.fill("id=username", "andy");
    page.fill("id=password", "panda<3");
    page.click("id=log-in");
    assertThat(page.locator(".element-header >> nth=0"))
      	.hasText("Financial Overview");
}

@techgirl1908

@AutomationPanda

Round 1: Selenium

@Test
public void testLogin()
{
    driver.get("https://demo.applitools.com/");
    driver.findElement(By.id("username")).sendKeys("angie");
    driver.findElement(By.id("password")).sendKeys("12345");
    driver.findElement(By.id("log-in")).click();
    assertEquals(
            "Financial Overview",
            driver.findElement(By.className("element-header")).getText());
}

Round 1: Selenium vs Playwright

@Test
public void testLogin()
{
    page.navigate("https://demo.applitools.com/");
    page.fill("id=username", "andy");
    page.fill("id=password", "panda<3");
    page.click("id=log-in");
    assertThat(page.locator(".element-header >> nth=0"))
      	.hasText("Financial Overview");
}
@Test
public void testLogin()
{
    driver.get("https://demo.applitools.com/");
    driver.findElement(By.id("username")).sendKeys("angie");
    driver.findElement(By.id("password")).sendKeys("12345");
    driver.findElement(By.id("log-in")).click();
    assertEquals(
            "Financial Overview",
            driver.findElement(By.className("element-header")).getText());
}

Round 2: Dropdowns

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

Round 2: Selenium

@Test
public void testDropdown()
{
    driver.get("https://kitchen.applitools.com/ingredients/select");
    
    Select dropdown = 
        new Select(driver.findElement(By.id("spices-select-single")));
        
    dropdown.selectByValue("ginger");
    
    assertEquals("ginger", 
        dropdown.getAllSelectedOptions().get(0).getAttribute("value"));
}

@techgirl1908

@AutomationPanda

Round 2: Playwright

@Test
public void testDropdown()
{
    page.navigate("https://kitchen.applitools.com/ingredients/select");
    page.selectOption("id=spices-select-single", "ginger");
    
    assertThat(page.locator("id=spices-select-single"))
    	.hasValue("ginger");
}

Round 2: Selenium vs Playwright

@Test
public void testDropdown()
{
    driver.get("https://kitchen.applitools.com/ingredients/select");
    
    Select dropdown = 
        new Select(driver.findElement(By.id("spices-select-single")));
        
    dropdown.selectByValue("ginger");
    
    assertEquals("ginger", 
        dropdown.getAllSelectedOptions().get(0).getAttribute("value"));
}
@Test
public void testDropdown()
{
    page.navigate("https://kitchen.applitools.com/ingredients/select");
    page.selectOption("id=spices-select-single", "ginger");
    
    assertThat(page.locator("id=spices-select-single"))
    	.hasValue("ginger");
}

Round 3:

Uploading Files

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

Round 3: Playwright

@Test
public void testFileUpload()
{
    page.navigate("https://kitchen.applitools.com/ingredients/file-picker");
    page.setInputFiles("id=photo-upload", Paths.get("pic.jpg"));
}

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

Round 3: Selenium

@Test
public void testFileUpload()
{
    driver.get("https://kitchen.applitools.com/ingredients/file-picker");
    driver.findElement(By.id("photo-upload")).sendKeys("/Users/angie/tmp/pic.jpg");
}

Round 3: Selenium vs Playwright

@Test
public void testFileUpload()
{
    driver.get("https://kitchen.applitools.com/ingredients/file-picker");
    driver.findElement(By.id("photo-upload")).sendKeys("/Users/angie/tmp/pic.jpg");
}
@Test
public void testFileUpload()
{
    page.navigate("https://kitchen.applitools.com/ingredients/file-picker");
    page.setInputFiles("id=photo-upload", Paths.get("pic.jpg"));
}

@techgirl1908

@AutomationPanda

Round 4: Frames

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

Round 4: Selenium

@Test
public void testIframe()
{
    driver.get("https://kitchen.applitools.com/ingredients/iframe");
    
    assertTrue(
    	driver.switchTo().frame("the-kitchen-table")
            .findElement(By.id("fruits-vegetables"))
            .isDisplayed());
}

@techgirl1908

Round 4: Playwright

@Test
public void testIframe()
{
    page.navigate("https://kitchen.applitools.com/ingredients/iframe");
    
    assertThat(page
    	.frameLocator("id=the-kitchen-table")
        .locator("id=fruits-vegetables")
    ).isVisible();
}

@AutomationPanda

Round 4: Selenium vs Playwright

@Test
public void testIframe()
{
    driver.get("https://kitchen.applitools.com/ingredients/iframe");
    
    assertTrue(
    	driver.switchTo().frame("the-kitchen-table")
            .findElement(By.id("fruits-vegetables"))
            .isDisplayed());
}
@Test
public void testIframe()
{
    page.navigate("https://kitchen.applitools.com/ingredients/iframe");
    
    assertThat(
    	page.frameLocator("id=the-kitchen-table")
        .locator("id=fruits-vegetables")
    ).isVisible();
}

Round 5: Waiting

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

Round 5: Playwright

@Test
public void testWaitForFilter()
{
    page.navigate("https://automationbookstore.dev/");
    page.fill("id=searchBar", "testing");
    
    page.locator("li.ui-screen-hidden >> nth=0").waitFor(
        new Locator.WaitForOptions()
            .setState(WaitForSelectorState.HIDDEN)
            .setTimeout(5000));
    
    assertThat(page.locator("li:not(.ui-screen-hidden)"))
      	.hasCount(1, new LocatorAssertions.HasCountOptions().setTimeout(5000));
}

@AutomationPanda

@techgirl1908

Round 5: Playwright (simplified)

@Test
public void testWaitForFilter()
{
    page.navigate("https://automationbookstore.dev/");
    page.fill("id=searchBar", "testing");
    
    // page.locator("li.ui-screen-hidden >> nth=0").waitFor(
    //    new Locator.WaitForOptions()
    //        .setState(WaitForSelectorState.HIDDEN)
    //        .setTimeout(5000));
    
    assertThat(page.locator("li:not(.ui-screen-hidden)"))
      	.hasCount(1, new LocatorAssertions.HasCountOptions().setTimeout(5000));
}

@AutomationPanda

Explicitly waiting for hidden elements to attach to the DOM is not required.
Waiting for the non-hidden element count to reach 1 inherently covers it.

@techgirl1908

Round 5: Selenium

@Test
public void testWaitForFilter()
{
    driver.get("https://automationbookstore.dev/");
    driver.findElement(By.id("searchBar")).sendKeys("testing");
    
    new WebDriverWait(driver, Duration.ofSeconds(5))
        .until(presenceOfElementLocated(
            By.cssSelector("li.ui-screen-hidden"))
        );
   
    assertEquals(1, 
        driver.findElements(By.cssSelector("li:not(.ui-screen-hidden)"))
            .size());
}

@AutomationPanda

@techgirl1908

Round 5: Selenium vs Playwright

@Test
public void testWaitForFilter()
{
    ...
    new WebDriverWait(driver, Duration.ofSeconds(5))
        .until(presenceOfElementLocated(
            By.cssSelector("li.ui-screen-hidden"))
        );
    ...
}
@Test
public void testWaitForFilter()
{
    ...
    page.locator("li.ui-screen-hidden >> nth=0").waitFor(
        new Locator.WaitForOptions()
            .setState(WaitForSelectorState.HIDDEN)
            .setTimeout(5000));
    ...
}

@AutomationPanda

Round 6: Alerts

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

Round 6: Selenium

@BeforeAll
public static void openAlertsPage(){
    driver.get("https://kitchen.applitools.com/ingredients/alert");
}

@Test
public void testAcceptAlert(){
    driver.findElement(By.id("alert-button")).click();
    driver.switchTo().alert().accept();
}

@Test
public void testDismissAlert(){
    driver.findElement(By.id("confirm-button")).click();
    driver.switchTo().alert().dismiss();
}

@Test
public void testAnswerPrompt(){
    driver.findElement(By.id("prompt-button")).click();
    Alert alert = driver.switchTo().alert();
    alert.sendKeys("nachos");
    alert.accept();
}

@techgirl1908

Round 6: Playwright

@AutomationPanda

@BeforeAll
public static void openAlertsPage(){
    page.navigate("https://kitchen.applitools.com/ingredients/alert");
}

@Test
public void testAcceptAlert(){
    page.onDialog(Dialog::accept);
    page.click("id=alert-button");
}

@Test
public void testDismissAlert(){
    page.onDialog(Dialog::dismiss);
    page.click("id=confirm-button");
}

@Test
public void testAnswerPrompt(){
    page.onDialog(dialog -> dialog.accept("nachos"));
    page.click("id=prompt-button");
}

Round 6: Selenium vs Playwright

@BeforeAll
public static void openAlertsPage(){
    driver.get("https://kitchen.applitools.com/ingredients/alert");
}

@Test
public void testAcceptAlert(){
    driver.findElement(By.id("alert-button")).click();
    driver.switchTo().alert().accept();
}

@Test
public void testDismissAlert(){
    driver.findElement(By.id("confirm-button")).click();
    driver.switchTo().alert().dismiss();
}

@Test
public void testAnswerPrompt(){
    driver.findElement(By.id("prompt-button")).click();
    Alert alert = driver.switchTo().alert();
    alert.sendKeys("nachos");
    alert.accept();
}
@BeforeAll
public static void openAlertsPage(){
    page.navigate("https://kitchen.applitools.com/ingredients/alert");
}

@Test
public void testAcceptAlert(){
    page.onDialog(Dialog::accept);
    page.click("id=alert-button");
}

@Test
public void testDismissAlert(){
    page.onDialog(Dialog::dismiss);
    page.click("id=confirm-button");
}

@Test
public void testAnswerPrompt(){
    page.onDialog(dialog -> dialog.accept("nachos"));
    page.click("id=prompt-button");
}

Round 7: Navigation

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

Round 7: Playwright

@Test
public void testNewTab()
{
    page.navigate("https://kitchen.applitools.com/ingredients/links");
    Page newTab = context.waitForPage(
        () -> page.click("id=button-the-kitchen-table"));
    assertThat(newTab.locator("id=fruits-vegetables")).isVisible();
}

@AutomationPanda

@techgirl1908

Round 7: Selenium

@Test
public void testNewTab()
{
    driver.get("https://kitchen.applitools.com/ingredients/links");
    driver.findElement(By.id("button-the-kitchen-table")).click();
    driver.getWindowHandles().forEach(tab->driver.switchTo().window(tab));
    assertTrue(driver.findElement(By.id("fruits-vegetables")).isDisplayed());
}

@AutomationPanda

@techgirl1908

Round 7: Selenium vs Playwright

@Test
public void testNewTab()
{
    driver.get("https://kitchen.applitools.com/ingredients/links");
    driver.findElement(By.id("button-the-kitchen-table")).click();
    driver.getWindowHandles().forEach(tab->driver.switchTo().window(tab));
    assertTrue(driver.findElement(By.id("fruits-vegetables")).isDisplayed());
}

@AutomationPanda

@Test
public void testNewTab()
{
    page.navigate("https://kitchen.applitools.com/ingredients/links");
    Page newTab = context.waitForPage(
        () -> page.click("id=button-the-kitchen-table"));
    assertThat(newTab.locator("id=fruits-vegetables")).isVisible();
}

Round 8: API Requests

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

https://kitchen.applitools.com/api/recipes

@techgirl1908

Round 8: Selenium

😫

@AutomationPanda

@techgirl1908

Round 8: Playwright

@AutomationPanda

Currently, Playwright provides API testing

only for JavaScript and Python.

 

API testing support will come to Java in 1.18.

It will come to .NET thereafter.

😲

@techgirl1908

Round 8: Playwright  (Python)

def test_kitchen_api_recipes(context):
  
  response = context.request.get(
    'https://kitchen.applitools.com/api/recipes')

  body = response.json()
  
  assert response.ok
  assert body['time'] > 0
  assert len(body['data']) > 0
  
  # Include other assertions about body data as appropriate

@AutomationPanda

@techgirl1908

Round 8: Selenium vs Playwright

def test_kitchen_api_recipes(context):
  
  response = context.request.get(
    'https://kitchen.applitools.com/api/recipes')

  body = response.json()
  
  assert response.ok
  assert body['time'] > 0
  assert len(body['data']) > 0
  
  # Include other assertions about body data as appropriate

@AutomationPanda

Round 9: Screenshots

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

Round 9: Playwright

@Test
public void testScreenshot()
{
    page.navigate("https://kitchen.applitools.com/ingredients/table");
    page.click("id=column-button-name");
    
    page.screenshot(
        new Page.ScreenshotOptions()
            .setPath(Paths.get("fullPage.png"))
            .setFullPage(true));
}

@AutomationPanda

@techgirl1908

Round 9: Selenium

@Test
public void testScreenshot()
{
    driver.get("https://kitchen.applitools.com/ingredients/table");
    driver.findElement(By.id("column-button-name")).click();
    
    var screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
    
    try{
        Files.move(screenshot, new File("resources/screenshots/test.png"));
    }catch(IOException e){
        e.printStackTrace();
    }
}

@AutomationPanda

@techgirl1908

Round 9: Selenium vs Playwright

@Test
public void testScreenshot(){
    driver.get("https://kitchen.applitools.com/ingredients/table");
    driver.findElement(By.id("column-button-name")).click();
    var screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
    try{
        Files.move(screenshot, new File("resources/screenshots/test.png"));
    }catch(IOException e){
        e.printStackTrace();
    }    
}
@Test
public void testScreenshot()
{
    page.navigate("https://kitchen.applitools.com/ingredients/table");
    page.click("id=column-button-name");
    
    page.screenshot(
        new Page.ScreenshotOptions()
            .setPath(Paths.get("fullPage.png"))
            .setFullPage(true));
}

@AutomationPanda

Round 10: Videos

@techgirl1908

@AutomationPanda

@techgirl1908

@AutomationPanda

@techgirl1908

Round 10: Selenium

@AutomationPanda

Selenium WebDriver does not support

video recording out of the box!

😫

@techgirl1908

Round 10: Playwright

@BeforeEach
public void startPage() {
    context = browser.newContext(
        new Browser.NewContextOptions()
            .setRecordVideoDir(Paths.get("videos/")));
            
    page = context.newPage();
}

@AfterEach
public void closePage() {
    context.close();
}

@Test
public void testLogin() {
    // This is the same code from Round 1
    ...
}

@AutomationPanda

@techgirl1908

Round 10: Selenium vs Playwright

@BeforeEach
public void startPage() {
    context = browser.newContext(
        new Browser.NewContextOptions()
            .setRecordVideoDir(Paths.get("videos/")));
            
    page = context.newPage();
}

@AfterEach
public void closePage() {
    context.close();
}

...

@AutomationPanda

Selenium WebDriver does not support

video recording out of the box!

@techgirl1908

Selenium or Playwright:

 

 

 

 

Who's the Winner?

@AutomationPanda

Selenium vs Playwright: Let the Code Speak

By Angie Jones

Selenium vs Playwright: Let the Code Speak

It's a battle of the code! Angie Jones represents Selenium WebDriver while Colby Fayock represents Cypress. Round for round, Angie and Colby take coding tasks and implement them in Selenium as well as Cypress, then analyze how each of the frameworks perform to solve real world testing challenges.

  • 5,745