Search code examples
javascriptplaywright

How to use try/catch with playwright page with click() and using timeout


I'm quite new to JS.

I have this code:

async function downloadOriginalFile(page, scanData, delay = 10000) {
  const { url, zipFname, caseId, scanId } = scanData;
  await page.goto(url);
  if (page.url().includes('/sign_in')) {
    console.log('Not logged in');
    throw new Error('Login failed');
  } else {
    console.log(`/${caseId}/${scanId} => Opened Scan Page`);
  }

  await page.waitForTimeout(delay);
  const page4Promise = page.waitForEvent('popup');
  const download2Promise = page.waitForEvent('download');

  // lines in question:
  await page.getByRole('main').getByRole('link').first().click(); // multi-grader
  await page.locator('div:nth-child(6) > a').waitForTimeout().click(); // solo grader

  const download = await download2Promise;
  console.log(`/${scanData.caseId}/${scanData.scanId} => Downloading original file`);
  await page.waitForTimeout(delay);

  await download.saveAs(path.join(path.dirname(zipFname), caseId, scanId, 'original_file', download.suggestedFilename()));
  // wait for the download and delete the temporary file
  await download.delete();
}

My problem is that I can only have one of those:

  await page.getByRole('main').getByRole('link').first().click(); // multi-grader
  // or
  await page.locator('div:nth-child(6) > a').waitForTimeout().click(); // solo grader

depending on the page was loaded. I'd like to use somehow try/catch with timeout to test whatever the two works after, say, 3 s. But I'm open to suggestions as well.

UPDATE: Based on @Ermi answer, I was able to better troubleshot my problem.

I found out that await page.getByRole('main').getByRole('link').first().click(); // multi-grader will also show up in solo-grader page, so I reverse the order.

Also, I should originally have used:

await page.locator('div:nth-child(6) > a').click(); // solo-grader`

This is what works if I load solo-grader page, even with page.getByRole('main')... there.

updated code:

async function downloadOriginalFile(page, scanData, delay = 10000) {
  const { url, zipFname, caseId, scanId } = scanData;
  await page.goto(url);
  if (page.url().includes('/sign_in')) {
    console.log('Not logged in');
    throw new Error('Login failed');
  } else {
    console.log(`/${caseId}/${scanId} => Opened Scan Page`);
  }

  await page.waitForTimeout(delay);
  const page4Promise = page.waitForEvent('popup');
  const download2Promise = page.waitForEvent('download');

  let download;
  try {
    // Try solo-grader
    await page.waitForTimeout(delay).locator('div:nth-child(6) > a').click(); // solo-grader
    console.log(`/${caseId}/${scanId} => Clicked on solo-grader link`);
    download = await download2Promise;
  } catch (error) {
    console.log(`Failed to click on solo-grader link: ${error.message}`);
    try {
      // Try multi-grader
      await page.getByRole('main').getByRole('link').first().click(); // multi-grader
      console.log(`/${caseId}/${scanId} => Clicked on multi-grader link`);
      download = await download2Promise;
    } catch (error) {
      console.log(`Failed to click on multi-grader link: ${error.message}`);
      throw new Error('Failed to download original file');
    }
  }

  console.log(`/${caseId}/${scanId} => Downloading original file`);
  await page.waitForTimeout(delay);

  await download.saveAs(path.join(path.dirname(zipFname), caseId, scanId, 'original_file', download.suggestedFilename()));
  // Wait for the download and delete the temporary file
  await download.delete();
}

However, this line: await page.waitForTimeout(delay).locator('div:nth-child(6) > a').click(); // solo-grader fails with:

Failed to click on solo grader link: page.waitForTimeout(...).locator is not a function

So basically, I want to try:

await page.locator('div:nth-child(6) > a').click(); // solo-grader`

for 10000 ms or so.


Solution

  • waitForTimeout is used to wait for a certain amount of time, and locator is used to locate elements on the page. They are separate functions and should not be chained together like that.

    You can achieve it using only { timeout } as the following:

    let download;
    try {
      // Try solo-grader
      const soloGraderLink = await page.locator('div:nth-child(6) > a');
      await soloGraderLink.click({ timeout: delay / 5 }); // solo-grader with timeout
      console.log(`/${caseId}/${scanId} => Clicked on solo-grader link`);
      download = await download2Promise;
    } catch (error) {
      console.log(`Failed to click on solo-grader link: ${error.message}`);
      try {
        // Try multi-grader
        await page.getByRole('main').getByRole('link').first().click(); // multi-grader
        console.log(`/${caseId}/${scanId} => Clicked on multi-grader link`);
        download = await download2Promise;
      } catch (error) {
        console.log(`Failed to click on multi-grader link: ${error.message}`);
        throw new Error('Failed to download original file');
      }
    }