Search code examples
typescriptplaywrightplaywright-test

Playwright - Select dropdown box


I am trying to select a dropdown box which is only displayed when a checkbox is clicked. I have used the xpath and it seems to pass on one browser and times out on other browsers. The browsers being used are chrome, safari and firefox. It is failing on Chrome and Safari, but passes on firefox.

Below is my code:

import { test, expect } from '@playwright/test';


test ("User can make a donation", async ({page}) => {
    await page.goto('https://app.pws.int.cruk.org/support-us/your-donation');

    await expect (page).toHaveTitle('Support us | Cancer Research UK')

    const amountBtn = page.locator('#amount10')
    const donationType = page.locator('#typeRadioGroup0')
    const selectMotivation = page.locator('//*[@id="main"]/form/div[3]/div/fieldset/div[1]/label/select')
    const destination = page.locator('#destinationRadioGroup1')


    await amountBtn.click({ force: true });
    await donationType.click({ force: true });
    await selectMotivation.selectOption('In memory of someone');
    await destination.click({ force: true });

    const selectCancer = page.locator('//*[@id="main"]/form/div[4]/div/fieldset/div[2]/select')
    await selectCancer.selectOption('Bowel cancer')

    const submit = page.locator('//*[@id="main"]/form/div[5]/div/div/button')
    await submit.click({ force: true });
    
})

I tried moving the xpath to after the checkbox has been clicked but I am still getting fails on multiple browsers. The log is below:

 1) [webkit] › donation.spec.ts:4:5 › User can make a donation ────────────────────────────────────

    Test timeout of 30000ms exceeded.

    Error: locator.selectOption: Page closed
    =========================== logs ===========================
    waiting for locator('xpath=//*[@id="main"]/form/div[4]/div/fieldset/div[2]/select')
    ============================================================

      20 |
      21 |     const selectCancer = page.locator('//*[@id="main"]/form/div[4]/div/fieldset/div[2]/select')
    > 22 |     await selectCancer.selectOption('Bowel cancer')
         |                        ^
      23 |
      24 |     const submit = page.locator('//*[@id="main"]/form/div[5]/div/div/button')
      25 |     await submit.click({ force: true });

  2) [chromium] › donation.spec.ts:4:5 › User can make a donation ──────────────────────────────────

    Test timeout of 30000ms exceeded.

    Error: locator.selectOption: Page closed
    =========================== logs ===========================
    waiting for locator('xpath=//*[@id="main"]/form/div[4]/div/fieldset/div[2]/select')
    ============================================================

      20 |
      21 |     const selectCancer = page.locator('//*[@id="main"]/form/div[4]/div/fieldset/div[2]/select')
    > 22 |     await selectCancer.selectOption('Bowel cancer')
         |                        ^
      23 |
      24 |     const submit = page.locator('//*[@id="main"]/form/div[5]/div/div/button')
      25 |     await submit.click({ force: true });

Can someone help me please?


Solution

  • Please avoid long hardcoded XPaths. Carefully read the Playwright locators docs and my blog post for the rationale. (The blog post is technically a Puppeteer post, but it pertains equally to Playwright in this case).

    In short, these long paths are very brittle, and don't account for dynamic changes in the page as elements are added and removed. They ignore much easier user-facing hooks into elements, like roles, text contents, test ids, and so forth.

    I'd suggest something like:

    import {expect, test} from "@playwright/test"; // ^1.39.0
    
    test("User can make a donation", async ({page}) => {
      test.setTimeout(20_000);
      const url = "<Your URL>";
      await page.goto(url, {waitUntil: "domcontentloaded"});
    
      const acceptCookieBtn = page.getByText("OK, continue to site");
      const amountBtn = page.locator("#amount10");
      const donationType = page.getByText("I am donating my own money");
      const selectMotivation = page.getByTestId("selectMotivation");
      const name = page.getByTestId("inMemoryName");
      const destination = page.getByText(
        "Choose a cancer type or an area of research"
      );
      const selectCancer = page.getByTestId("restrictionSelect");
      const submitBtn = page.getByText("Continue", {exact: true});
    
      await expect(page).toHaveTitle("Support us | Cancer Research UK");
      await acceptCookieBtn.click({timeout: 5000}).catch(() => {});
      await amountBtn.click();
      await donationType.click({force: true});
      await selectMotivation.selectOption("In memory of someone");
      await name.type("foobar");
      await destination.click({force: true});
      await selectCancer.selectOption("Bowel cancer");
    
      await page.screenshot({path: "verify1.png", fullPage: true});
    
      // optionally move on to the next page:
      await submitBtn.click();
      await page.waitForURL("**/support-us/details");
      await page.screenshot({path: "verify2.png", fullPage: true});
    });