Playwright 无法在一次测试中注销并以新用户身份重新登录

问题描述 投票:0回答:1

我正在尝试使用基于模型的测试方法,将 xstate 与 playwright 集成。我们的系统使用一个工作流程,其中 2 个用户可以登录系统并响应彼此的操作以完成工作流程。

当我尝试使用同一浏览器注销并再次登录时,浏览器以第一个用户身份重新登录,就好像当我注销并默认返回到它时无法清除经过身份验证的会话,即使我输入了不同用户的详细信息

添加一个在测试之间完全关闭浏览器的 afterEach 似乎是单次登录测试的一个解决方案,因为这将清除所有 cookie,并且我能够完成第二个测试,以第二个用户身份登录,但是这很快又失败了,因为我没有在各个测试步骤之间的不同登录之间关闭浏览器。

问题是我找不到能够控制/引用浏览器或在测试模型中打开多个浏览器的方法,因为浏览器实例是稍后在测试挂钩中创建的。我想找到一种方法,能够在步骤之间关闭并重新打开浏览器窗口,最好将其合并到我的登录/注销功能中,或者当我注销以正确清除会话时,我可能会错过一些步骤,然后使用新用户重新登录

下面是我的钩子,然后是 xstate 用于运行测试的内容,位于我的脚本底部

`test.beforeEach(async ({ browser }) => {
  page = await browser.newPage();
  await page.goto(URL);
  testNum++
});

test.afterEach(async ({browser}) => {
  for(let context of browser.contexts())
    await context.close()
});

test.describe('CE workflows', () => {
  const testPlans = testModel.getSimplePathPlans();

  testPlans.forEach((plan, i) => {
    plan.paths.forEach((path, i) => {
      test(path.description, async ({}) => {
        await path.test(page);
      });
    });
  });
})`

这些是我在每个测试步骤中使用的登录/注销功能

`async function login(email) {
  await page.goto(URL);
  await page.getByTestId('loginEmail').fill(email);
  await page.getByTestId('loginPassword').fill('password');
  await page.getByTestId('btnLogin').click();

  if (await page.getByRole('button', { name: 'Allow cookies' }).isVisible())
  await page.getByRole('button', { name: 'No thanks' }).click();
}

async function logout() {
  await page.getByTestId('btnOptions').click();
  await page.getByRole('button', { name: 'Logout' }).click();
}`

这是我模型中的第一个边 每个步骤都与此类似,即登录,执行用户操作,然后注销,准备传递给第二个用户

`const testModel = createModel(CEWorkflowMachine, {
  events: {
    CREATE_CE_CLIENT: async () => {
      await login()

      await page.goto(URL);
      await page.getByRole('link', { name: 'Create compensation event' }).click();

      await page.getByLabel('Title').fill(`Test for model based CE ${testNum}`);
      await page.getByLabel('Description').fill('Test Description');
      await page.getByLabel('Project manager\'s assumptions').fill('Test Assumption');
      await page.getByText('The Project Manager gives an').click();

      await page.getByText('Notify').click();

      await logout()
    },`
typescript automated-tests playwright xstate model-based-testing
1个回答
0
投票

这里很少有标志,其中一个或多个可能会导致这种行为。最终,如果它以以前的用户身份登录,则您会遇到测试隔离或某些异步/执行顺序问题。

混乱的测试隔离

您当前执行此操作是为了管理浏览器页面:

test.beforeEach(async ({ browser }) => {
  page = await browser.newPage();
  await page.goto(URL);
  testNum++
})

这意味着

page
是范围内可用的变量。如果它不是由
let page
定义的,情况会更糟,因为这意味着它实际上完全是全球性的。

即使不是全局的,保留可变引用也会带来麻烦。如果您有文件内并行化或想稍后添加它,您将会遇到问题。添加您可能会意外引用此变量;或用封口将其封闭;考虑到很多事情都是异步的,你可以得出结论,这是非常危险的,管理起来也很糟糕。

虽然有可能拥有这一点并完美地管理它,但您正在向一整类现在可能出现的错误敞开大门。

page
附加到浏览器上下文,这是剧作家提供的用于隔离测试的构造。如果你有机会在测试之间重用它,那就有问题了。

我强烈怀疑您的情况很可能如此。状态机正在“关闭”您设置的

page
变量。即使您重新分配
page
,它们仍然会引用旧的
page
对象引用。

您应该完全废弃这个范围广泛的

page
var。相反,请确保您使用
page
传递给测试处理程序/挂钩:

test.beforeEach(async ({ page }) => {
  await page.goto(URL);
});

test.describe('CE workflows', () => {
  const testPlans = testModel.getSimplePathPlans();

  testPlans.forEach((plan, i) => {
    plan.paths.forEach((path, i) => {
      test(path.description, async ({page}) => {
        await path.test(page);
      });
    });
  });
})`

如果您的测试被正确隔离,您不需要手动关闭浏览器。所以我删除了它。提供给

page
方法(和钩子)的
test
已经自动确定了相关测试的范围。确保将此传递到任何共享方法,并且您没有在任何地方使用全局或广泛范围的页面。

这意味着您的状态机将需要提供页面。可能那些机器需要通过它。请注意,这是官方文档中的做法

无需致电

newPage
,因为这已经发生了。

登录/注销不等待

当您在注销和登录按钮上等待

click()
时,单击该按钮后就会解决该问题。它不会自动等到下一页。

您的登录情况没问题,因为之后它当然会等待与登录页面进行交互。

但是对于注销,取决于流程是否复杂或较长,这意味着它单击注销按钮并立即结束测试并杀死浏览器上下文。注销可能没有时间完成。

© www.soinside.com 2019 - 2024. All rights reserved.