typescript 所有等级库文件的每个文件之前的Playwright

hc2pp10m  于 2023-03-19  发布在  TypeScript
关注(0)|答案(3)|浏览(114)

我对Playwright非常陌生。由于我的测试套件,我需要在运行每个测试之前登录到我的应用程序。在一个简单的规范文件中,我可以简单地调用test.beforeEach。我的问题是:我需要在每次测试前登录每个规格文件.

test.describe('Test', () => {
    //I need to make the code inside this beforeEach a exported 
    //function to call inside the before each of every spec file I have
    test.beforeEach(async ({ page }) => {
        await page.goto('/login');
        await page.click('text=Log in with Google account');
        await page.fill('id=identifierId', LoginAutomationCredentials.USER);
        await page.click('button[jsname="LgbsSe"]');
        await page.fill('input[type="password"]', LoginAutomationCredentials.PASSWORD);
        await page.click('button[jsname="LgbsSe"]');
        const otp = authenticator.generateToken(LoginAutomationCredentials.TOKEN);
        await page.fill('id=totpPin', otp);
        await page.click('button[jsname="LgbsSe"]');
    });

    it('Some description', async ({ page }) => {
        await page.goto('/foo');
        const dateFilter = await page.inputValue('input[placeholder="2011/03/02"]');
        expect(dateFilter).toBe('2021/12/07');
    });
});

我试着简单地把代码变成一个函数,放在一个单独的.ts文件中,然后导入它,但是我认为上下文是必要的,这可能是每个使用playwright的测试人员都知道并经常使用的,但是,我没有找到任何关于这个主题的东西。
我怎样才能避免复制beforeEach的整个代码并将其粘贴到我所有的spec文件中?我怎样才能使它成为一个函数并随时调用它?

irtuqstp

irtuqstp1#

使用固定装置。
fixture.js:

const base = require('@playwright/test')
const newTest = base.test.extend({
    login: async({page}, use) => {
        await login();
        await use(page); //runs test here
        //logic after test
    }
})
exports.newTest = newTest
exports.expect = newTest.expect

然后在测试中:

const {newTest} = require('fixture.js')
newTest('mytest', async ({login}) => {
    //test logic
    login.goto(''); // using login here since I pass it as page in the fixture.
})
jvlzgdj9

jvlzgdj92#

Fixtures是正确的方法,但有一个更好的选择,而不是在你想登录时必须记住使用login而不是page
首先,我在playwright/src/index.ts中设置了项目的所有固定装置:

import { test as base_test, expect } from 'playwright_test';

type TestFixtures = {
  user: string;
};

export const test = base_test.extend<TestFixtures>( {
  user : 'default-user@example.com',
  async context( { user, context, request }, use ) {
    // This makes a REST request to the backend to get a JWT token
    // and then stores that token in the browsers localStorage,
    // but you could replace this with whatever makes sense for auth
    // in your app
    if ( user ) {
      const content = await getAuthScript( user, request );
      await context.addInitScript( { content } );
    }
    await use( context );
  },
} );

/**
 * This makes a REST request to the backend to get a JWT token
 * and then stores that token in the browsers localStorage,
 * but you could replace this with whatever makes sense for auth
 * in your app.
 */
async function getAuthScript( user, request ) {
  const res = await request.post( '/api/test/auth', { data : { user } } );
  const { token } = await res.json();
  return `window.localStorage.setItem( 'jwt-token', "${token}" );`;
}

export { test, expect }

我还确保playwright/tsconfig.json包含以下内容:

{
  "extends": "../path/to/base/tsconfig.json",
  "compilerOptions": {
    "noEmit": true,
    "paths": {
      "~": [ "./src" ],
      "~*": [ "./src/*" ]
    },
    "baseUrl": ".",
    "rootUrl": ".",
  },
  "include": [
    "src/**/*.ts",
    "test/**/*.ts"
  ],
}

现在每个测试都将自动以default-user@example.com的身份登录,但是如果您需要一个测试以不同的用户身份登录,那么您需要在该测试文件中做的就是:

import { test, expect } from '~';

test.use( { user : 'somebody-else@example.com' } );
test( 'can login as somebody-else', async ( { page } ) => {
  // Run your tests here...
} );
1l5u6lss

1l5u6lss3#

我认为全局设置和拆卸可能是测试前登录的最佳解决方案。
下面是一个草图,根据文档(未经测试),显示了这在您的代码中可能是什么样子。
global-setup.ts

import {chromium, FullConfig} from "@playwright/test";

async function globalSetup(config: FullConfig) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // do your login:
  await page.goto("/login");
  await page.click("text=Log in with Google account");
  await page.fill(
    "id=identifierId",
    LoginAutomationCredentials.USER
  );
  await page.click('button[jsname="LgbsSe"]');
  await page.fill(
    'input[type="password"]',
    LoginAutomationCredentials.PASSWORD
  );
  await page.click('button[jsname="LgbsSe"]');
  const otp = authenticator.generateToken(
    LoginAutomationCredentials.TOKEN
  );
  await page.fill("id=totpPin", otp);
  await page.click('button[jsname="LgbsSe"]');

  // see below for further discussion
  // const { baseURL, storageState } = config.projects[0].use;
  // await page.context().storageState({ path: storageState as string });
  // await browser.close();
}

export default globalSetup;

playwright.config.ts

import {defineConfig} from "@playwright/test";

export default defineConfig({
  globalSetup: require.resolve("./global-setup"),
  use: {
    baseURL: "http://localhost:3000/",
    storageState: "state.json",
  },
});

tests/foo.spec.js

test.describe("Test", () => {
  it("Some description", async ({page}) => {
    await page.goto("/foo");
    const dateFilter = await page.inputValue(
      'input[placeholder="2011/03/02"]'
    );
    expect(dateFilter).toBe("2021/12/07");
  });
});

现在,在最初的Playwright示例中,他们的globalSetup函数写入存储状态:

// ...
  await page.getByText('Sign in').click();
  await page.context().storageState({ path: storageState as string });
  await browser.close();
// ...

但是您的代码没有这样做,您可能需要在globalSetup函数的最后一行这样做,然后调用await browser.close()来保存这些令牌,并在一个浏览器会话和下一个会话之间携带这些令牌。
他们从const { baseURL, storageState } = config.projects[0].use;中提取这个storageState字符串,这样就不需要在多个地方硬编码。
一旦你让它工作并且你已经写了你的state.json文件,如果令牌仍然有效,你可能想跳过运行未来的登录。你可以通过对global-setup.ts做如下修改来做到这一点:

import fs from "node:fs/promises";

// ...

async function exists(path: string): Promise<boolean> {
  return !!(await fs.stat(path).catch(() => false));
}

async function globalSetup(config: FullConfig) {
  const {storageState} = config.projects[0].use;

  if (await exists(storageState)) {
    return;
  }

  const browser = await chromium.launch();
  const page = await browser.newPage();

  // do your login:
  await page.goto("/login");

  // ...
}

// ...

最有可能的是,state.json应该添加到项目的.gitignore中。

相关问题