typescript 在Playwright中有条件地等待定位器

wvt8vs2t  于 2023-03-13  发布在  TypeScript
关注(0)|答案(2)|浏览(224)

我正在使用Playwright测试一个登录表单,该表单具有以下两种可能状态之一:
1.如果用户已经存在,它只会询问他们的密码并显示“登录”按钮。
1.如果是新用户,它会询问他们的“名和姓”和密码,并显示“保存”按钮。
由于身份验证系统的某些逻辑,使其完全确定是不切实际的,因此我需要确保我的测试成功地处理了这些情况中的每一种。
1(https://stackoverflow.com/a/71374820/6090140)就是这样一种解决方案:

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();
}

但是,这需要页面等待超时后再继续,我可以减少条件检查2(https://stackoverflow.com/a/67922197/6090140)的超时,但是这会增加测试的脆弱性。
如何使用条件逻辑而不强制超时,以确保Playwright中的两个代码路径之一被执行?

wlp8pajw

wlp8pajw1#

使用Promise.race,你可以等待许多可能的定位器中的一个可见,然后返回关于哪个定位器成功的细节。

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;
}

下面是如何用它来解决这个问题:

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();
}

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

9gm1akwq

9gm1akwq2#

这是我发现的根据哪个定位器显示来确定状态的最简单方法:

const accountExists = await Promise.any([
    page.getByRole('button', { name: 'Sign In' }).waitFor().then(() => false),
    page.getByRole('button', { name: 'Save' }).waitFor().then(() => true),
]).catch(() => {
    throw "Missing button";
});

然后,您可以根据需要使用它:

if (accountExists) {
    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();
}

相关问题