有条件地等待 Playwright 中的定位器

Rya*_*ers 5 javascript typescript playwright

我正在使用 Playwright 测试一个登录表单,该表单具有以下两种可能的状态之一:

  1. 如果用户已经存在,它只会询问他们的密码并显示“登录”按钮。
  2. 如果是新用户,它将询问他们的“名字和姓氏”和密码,并显示“保存”按钮。

由于我们的身份验证系统的某些逻辑,完全确定性是不切实际的,因此我需要确保我的测试成功处理每种情况。

一种这样的解决方案是[1]

if (await page.getByRole('button', { name: 'Sign In' }).count() > 0) {
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign In' }).click();
} else {
  await page.getByLabel('First & last name').fill('Bob Alice');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Save' }).click();
}
Run Code Online (Sandbox Code Playgroud)

但是,这需要页面等待我的超时才能继续。我可以减少条件检查的超时[2],但这会增加测试的脆弱性。

如何在不强制超时的情况下使用条件逻辑来确保执行 Playwright 中的两个代码路径之一?

Rya*_*ers 7

使用Promise.race,您可以等待许多可能的定位器之一可见,然后返回有关哪个定位器成功的详细信息。这是 Typescript 中的这样一个辅助函数:

import { Locator } from '@playwright/test';

type WaitForRes = [ locatorIndex: number, locator: Locator ];

export async function waitForOneOf(
  locators: Locator[],
): Promise<WaitForRes> {
  const res = await Promise.race([
    ...locators.map(async (locator, index): Promise<WaitForRes> => {
      let timedOut = false;
      await locator.waitFor({ state: 'visible' }).catch(() => timedOut = true);
      return [ timedOut ? -1 : index, locator ];
    }),
  ]);
  if (res[0] === -1) {
    throw new Error('no locator visible before timeout');
  }
  return res;
}
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它来解决问题:

const [ index ] = await waitForOneOf([
  page.getByRole('button', { name: 'Sign In' }),
  page.getByRole('button', { name: 'Save' }),
]);
const isExistingAccount = index === 0;
if (isExistingAccount) {
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign In' }).click();
} else {
  await page.getByLabel('First & last name').fill('Bob Alice');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Save' }).click();
}
Run Code Online (Sandbox Code Playgroud)

与简单地使用[1]相比,包装waitForOneOf器具有一些好处:Promise.race

  1. 预期除了一个定位器之外的所有定位器都会超时并抛出错误。这不是问题,因为将Promise.race丢弃那些拒绝;但是,如果您在 VS Code(或其他地方)中进行调试,则此抛出的错误将触发一个暂停代码执行的断点。为了减轻这种情况,我们.catch改用错误。
  2. 通过捕获错误,我们还可以指定其失败,如果没有定位器正确解析 ( if (res[0] === -1)),那么我们仍然可以抛出错误。
  3. 通过返回解析的定位器及其索引,我们可以执行有条件的代码,如上所示。