Playwright如何从创建浏览器上下文的函数中返回页面?

huangapple go评论86阅读模式
英文:

Playwright How To Return page from a function that creates a browser context?

问题

以下是您要翻译的内容:

我正在尝试创建一个函数该函数打开浏览器并使用基本身份验证登录页面我希望该函数返回页面以便我可以在测试中使用它并继续使用它

当我将`browser`传递给函数时我需要在函数内部创建一个新的上下文以便我可以使用基本身份验证登录

在函数中创建一个新的浏览器上下文可以正常打开页面并登录

问题在于我无法从函数中返回新页面从函数返回的页面没有智能提示并在我尝试以正常方式使用它时失败例如执行`page.locator('#id1').click()`导致测试失败

// 自定义打开浏览器登录功能
export async function openBrowserAndLogin(browser, username, password){

  const context = await browser.newContext({
    httpCredentials:{
      username: username,
      password: password
    },
  })

  const page = await context.newPage()
  await page.goto('websiteurl.com')
  return page
}

// 测试
import { test } from '@playwright/test';
import {openBrowserAndLogin} from '../customfunctions.openBrowser.mjs';

test('登录并执行操作', async ({ browser }) => {
  
  const page = openBrowserAndLogin(browser,'user1', 'secretpassword')

  page.locator('#account').click() // 此页面对象上没有任何方法???测试失败
  
})

希望这有所帮助。如果您需要更多信息,请随时提问。

英文:

I am trying to make a function that opens the browser and logs into the page using basic auth. I would like the function to return the page so I can pick it up in the test and continue to use it.

When I pass browser to the function I need to create a new context inside the function so I can login with basic auth.

Creating a new browser context in the function works fine to open the page and login.

The problem is that I cannot return the new page from the function. The page that is returned from the function has no intellisense and fails when I attempt to use it in the normal way --- such as doing: page.locator('#id1').click() ---> test fails

// Custom Open Browser Login Function
export async function openBrowserAndLogin(browser, username, password){

  const context = await browser.newContext({
    httpCredentials:{
      username: username,
      password: password
    },
  })

  const page = await context.newPage()
  await page.goto('websiteurl.com')
  return page
}


// Test
import { test } from '@playwright/test';
import {openBrowserAndLogin} from '../customfunctions.openBrowser.mjs'

test('login and do stuff', async ({ browser }) => {
  
  const page = openBrowserAndLogin(browser,'user1', 'secretpassword')

  page.locator('#account').click() // no methods exist on this page object???? Test Fail
  
})

So basically I am importing a function into the test. I pass the browser to the function. The function uses browser to create a new context, logs into application, and returns a new page.

The page returned from the function is dead.

Does anyone know a way to return a page from a context created inside an imported function?

Or if there is some other way to accomplish this that would be helpful too.

答案1

得分: 3

openBrowserAndLogin 不是一个好的模式。这会放弃对浏览器上下文对象的引用,因此您永远无法关闭它,从而导致内存泄漏并可能使进程挂起(除非测试套件以不友好的方式为您终止它)。

相反,建议让 Playwright 管理 page

test('登录并执行操作', async ({ page }) => {
//                              ^^^^

现在,您可以使用 Playwright 的 configtest.use 来添加凭据:

import {test} from "@playwright/test"; // ^1.30.0

test.describe("带有凭据", () => {
  test.use({
    httpCredentials: {
      username: "user1",
      password: "secretpassword"
    }
  });

  test("登录并执行操作", async ({page}) => {
    await page.goto("https://example.com/");
    await page.locator("#account").click();
  });
});

请注意,我使用了 await 来等待 page.locator('#account').click()

另一个选项是使用 fixtures

英文:

openBrowserAndLogin is not a good pattern. This abandons the reference to the browser context object so you can never close it, thereby leaking memory and potentially hanging the process (unless the test suite ungracefully terminates it for you).

Instead, prefer to let Playwright manage the page:

test('login and do stuff', async ({ page }) => {
//                                  ^^^^

Now you can add credentials with Playwright's config or test.use:

import {test} from "@playwright/test"; // ^1.30.0

test.describe("with credentials", () => {
  test.use({
    httpCredentials: {
      username: "user1",
      password: "secretpassword"
    }
  });

  test("login and do stuff", async ({page}) => {
    await page.goto("https://example.com/");
    await page.locator("#account").click();
  });
});

Notice that I've awaited page.locator('#account').click().

Another option is to use fixtures.

答案2

得分: 3

你只是缺少await

openBrowserAndLogin是一个async函数,因此它返回一个Promise,不会具有页面方法本身。你需要首先解包它,就像这样:

const page = await openBrowserAndLogin(browser, 'user1', 'secretpassword')

话虽如此,如果你需要每个测试都进行相同的登录,我绝对建议使用带有storageState的全局设置中进行身份验证,然后只使用page fixture,或者你还可以覆盖page fixture或添加自己的fixture等。还有其他潜在的方法。但就你的情况而言,只缺少这一小部分。

请注意,如果你手动创建上下文,最好是close你的上下文。

英文:

You’re just missing await.

openBrowserAndLogin is an async function, so it’s returning a promise, which wouldn’t have the page methods itself. You need to unwrap it first, like so:

const page = await openBrowserAndLogin(browser,'user1', 'secretpassword')

That being said, I would definitely recommend doing the auth in global setup with storageState if you need the same login for every test and then just use the page fixture, or you could always override the page fixture or add your own or something similar. There are other potential ways too. But for what you have, just that small piece was missing.

Note that it’s also good practice to close your context if you manually create one.

huangapple
  • 本文由 发表于 2023年2月18日 09:00:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75490513.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定