PageSourceSearch

https://playwright.dev/assets/js/1b15ab86.b3256a97.js

js playwright.dev collected 2026-09-24 06:56:41 UTC 15,452 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkplaywright_dev=self.webpackChunkplaywright_dev||[]).push([["5688"],{46074(e,n,t){t.r(n),t.d(n,{metadata:()=>a,default:()=>m,frontMatter:()=>o,contentTitle:()=>c,toc:()=>p,assets:()=>d});var a=JSON.parse('{"id":"test-parameterize","title":"Parameterize tests","description":"Introduction","source":"@site/versioned_docs/version-stable/test-parameterize.mdx","sourceDirName":".","slug":"/test-parameterize","permalink":"/docs/test-parameterize","draft":false,"unlisted":false,"tags":[],"version":"stable","frontMatter":{"id":"test-parameterize","title":"Parameterize tests"},"sidebar":"docs","previous":{"title":"Parallelism","permalink":"/docs/test-parallel"},"next":{"title":"Projects","permalink":"/docs/test-projects"}}'),s=t(74848),r=t(28453),i=t(13554),l=t(41647);t(83137);let o={id:"test-parameterize",title:"Parameterize tests"},c,d={},p=[{value:"Introduction",id:"introduction",level:2},{value:"Parameterized Tests",id:"parameterized-tests",level:2},{value:"Before and after hooks",id:"before-and-after-hooks",level:3},{value:"Parameterized Projects",id:"parameterized-projects",level:2},{value:"Passing Environment Variables",id:"passing-environment-variables",level:2},{value:".env files",id:"env-files",level:3},{value:"Create tests via a CSV file",id:"create-tests-via-a-csv-file",level:2}];function h(e){let n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,s.jsx)(n.p,{children:"You can either parameterize tests on a test level or on a project level."}),"\n",(0,s.jsx)(n.h2,{id:"parameterized-tests",children:"Parameterized Tests"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="example.spec.ts"',children:"[\n  { name: 'Alice', expected: 'Hello, Alice!' },\n  { name: 'Bob', expected: 'Hello, Bob!' },\n  { name: 'Charlie', expected: 'Hello, Charlie!' },\n].forEach(({ name, expected }) => {\n  // You can also do it with test.describe() or with multiple tests as long the test name is unique.\n  test(`testing with ${name}`, async ({ page }) => {\n    await page.goto(`https://example.com/greet?name=${name}`);\n    await expect(page.getByRole('heading')).toHaveText(expected);\n  });\n});\n"})}),"\n",(0,s.jsx)(n.h3,{id:"before-and-after-hooks",children:"Before and after hooks"}),"\n",(0,s.jsxs)(n.p,{children:["Most of the time you should put ",(0,s.jsx)(n.code,{children:"beforeEach"}),", ",(0,s.jsx)(n.code,{children:"beforeAll"}),", ",(0,s.jsx)(n.code,{children:"afterEach"})," and ",(0,s.jsx)(n.code,{children:"afterAll"})," hooks outside of ",(0,s.jsx)(n.code,{children:"forEach"}),", so that hooks are executed just once:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="example.spec.ts"',children:"test.beforeEach(async ({ page }) => {\n  // ...\n});\n\ntest.afterEach(async ({ page }) => {\n  // ...\n});\n\n[\n  { name: 'Alice', expected: 'Hello, Alice!' },\n  { name: 'Bob', expected: 'Hello, Bob!' },\n  { name: 'Charlie', expected: 'Hello, Charlie!' },\n].forEach(({ name, expected }) => {\n  test(`testing with ${name}`, async ({ page }) => {\n    await page.goto(`https://example.com/greet?name=${name}`);\n    await expect(page.getByRole('heading')).toHaveText(expected);\n  });\n});\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If you want to have hooks for each test, you can put them inside a ",(0,s.jsx)(n.code,{children:"describe()"})," - so they are executed for each iteration / each individual test:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="example.spec.ts"',children:"[\n  { name: 'Alice', expected: 'Hello, Alice!' },\n  { name: 'Bob', expected: 'Hello, Bob!' },\n  { name: 'Charlie', expected: 'Hello, Charlie!' },\n].forEach(({ name, expected }) => {\n  test.describe(() => {\n    test.beforeEach(async ({ page }) => {\n      await page.goto(`https://example.com/greet?name=${name}`);\n    });\n    test(`testing with ${expected}`, async ({ page }) =>
1 {\n      await expect(page.getByRole('heading')).toHaveText(expected);\n    });\n  });\n});\n"})}),"\n",(0,s.jsx)(n.h2,{id:"parameterized-projects",children:"Parameterized Projects"}),"\n",(0,s.jsx)(n.p,{children:"Playwright Test supports running multiple test projects at the same time. In the following example, we'll run two projects with different options."}),"\n",(0,s.jsxs)(n.p,{children:["We declare the option ",(0,s.jsx)(n.code,{children:"person"})," and set the value in the config. The first project runs with the value ",(0,s.jsx)(n.code,{children:"Alice"})," and the second with the value ",(0,s.jsx)(n.code,{children:"Bob"}),"."]}),"\n",(0,s.jsxs)(i.A,{groupId:"js-flavor",defaultValue:"ts",values:[{label:"TypeScript",value:"ts"},{label:"JavaScript",value:"js"}],children:[(0,s.jsx)(l.A,{value:"ts",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="my-test.ts"',children:"import { test as base } from '@playwright/test';\n\nexport type TestOptions = {\n  person: string;\n};\n\nexport const test = base.extend<TestOptions>({\n  // Define an option and provide a default value.\n  // We can later override it in the config.\n  person: ['John', { option: true }],\n});\n"})})}),(0,s.jsx)(l.A,{value:"js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="my-test.js"',children:"const base = require('@playwright/test');\n\nexports.test = base.test.extend({\n  // Define an option and provide a default value.\n  // We can later override it in the config.\n  person: ['John', { option: true }],\n});\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["We can use this option in the test, similarly to ",(0,s.jsx)(n.a,{href:"/docs/test-fixtures",children:"fixtures"}),"."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="example.spec.ts"',children:"import { test } from './my-test';\n\ntest('test 1', async ({ page, person }) => {\n  await page.goto(`/index.html`);\n  await expect(page.locator('#node')).toContainText(person);\n  // ...\n});\n"})}),"\n",(0,s.jsx)(n.p,{children:"Now, we can run tests in multiple configurations by using projects."}),"\n",(0,s.jsxs)(i.A,{groupId:"js-flavor",defaultValue:"ts",values:[{label:"TypeScript",value:"ts"},{label:"JavaScript",value:"js"}],children:[(0,s.jsx)(l.A,{value:"ts",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="playwright.config.ts"',children:"import { defineConfig } from '@playwright/test';\nimport type { TestOptions } from './my-test';\n\nexport default defineConfig<TestOptions>({\n  projects: [\n    {\n      name: 'alice',\n      use: { person: 'Alice' },\n    },\n    {\n      name: 'bob',\n      use: { person: 'Bob' },\n    },\n  ]\n});\n"})})}),(0,s.jsx)(l.A,{value:"js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="playwright.config.ts"',children:"// @ts-check\n\nmodule.exports = defineConfig({\n  projects: [\n    {\n      name: 'alice',\n      use: { person: 'Alice' },\n    },\n    {\n      name: 'bob',\n      use: { person: 'Bob' },\n    },\n  ]\n});\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["We can also use the option in a fixture. Learn more about ",(0,s.jsx)(n.a,{href:"/docs/test-fixtures",children:"fixtures"}),"."]}),"\n",(0,s.jsxs)(i.A,{groupId:"js-flavor",defaultValue:"ts",values:[{label:"TypeScript",value:"ts"},{label:"JavaScript",value:"js"}],children:[(0,s.jsx)(l.A,{value:"ts",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="my-test.ts"',children:"import { test as base } from '@playwright/test';\n\nexport type TestOptions = {\n  person: string;\n};\n\nexport const test = base.extend<TestOptions>({\n  // Define an option and provide a default value.\n  // We can later override it in the config.\n  person: ['John', { option: true }],\n\n  // Override default \"page\" fixture.\n  page: async ({ page, person }, use) => {\n    await page.goto('/chat');\n    // We use \"person\" parameter as a \"name\" for the chat room.\n    await page.getByLabel('User Name').fill(person);\n    await page.getByText('Enter chat room').click();\n    // Each test will get a \"page\" that already has the person name.\n    await use(page);\n  },\n});\n"})})}),(0,s.jsx)(l.A,{value:"js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="my-test.js"',children:"const base = require('@playwright/test');\n\nexports.test = base.test.extend({\n  // Define an option and provide a default value.\n  // We can later override it in the config.\n  person: ['John', { option: true }],\n\n  // Override default \"page\" fixture.\n  page: async ({ page, person }, use) => {\n    await page.goto('/chat');\n    // We use \"person\" parameter as a \"name\" for the chat room.\n    await page.getByLabel('User Name').fill(person);\n    await page.getByText('Enter chat room').click();\n    // Each test will get a \"page\" that already has the person name.\n    await use(page);\n  },\n});\n"})})})]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Parameterized projects behavior has changed in version 1.18. ",(0,s.jsx)(n.a,{href:"./release-notes#breaking-change-custom-config-options",children:"Learn more"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"passing-environment-variables",children:"Passing Environment Variables"}),"\n",(0,s.jsx)(n.p,{children:"You can use environment variables to configure tests from the command line."}),"\n",(0,s.jsx)(n.p,{children:"For example, consider the following test file that needs a username and a password. It is usually a good idea not to store your secrets in the source code, so we'll need a way to pass secrets from outside."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="example.spec.ts"',children:"test(`example test`, async ({ page }) =>
1 {\n  // ...\n  await page.getByLabel('User Name').fill(process.env.USER_NAME);\n  await page.getByLabel('Password').fill(process.env.PASSWORD);\n});\n"})}),"\n",(0,s.jsx)(n.p,{children:"You can run this test with your secret username and password set in the command line."}),"\n",(0,s.jsxs)(i.A,{groupId:"bash-flavor",defaultValue:"bash",values:[{label:"Bash",value:"bash"},{label:"PowerShell",value:"powershell"},{label:"Batch",value:"batch"}],children:[(0,s.jsx)(l.A,{value:"bash",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"USER_NAME=me PASSWORD=secret npx playwright test\n"})})}),(0,s.jsx)(l.A,{value:"powershell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"$env:USER_NAME=me\n$env:PASSWORD=secret\nnpx playwright test\n"})})}),(0,s.jsx)(l.A,{value:"batch",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:"set USER_NAME=me\nset PASSWORD=secret\nnpx playwright test\n"})})})]}),"\n",(0,s.jsx)(n.p,{children:"Similarly, configuration file can also read environment variables passed through the command line."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="playwright.config.ts"',children:"import { defineConfig } from '@playwright/test';\n\nexport default defineConfig({\n  use: {\n    baseURL: process.env.STAGING === '1' ? 'http://staging.example.test/' : 'http://example.test/',\n  }\n});\n"})}),"\n",(0,s.jsx)(n.p,{children:"Now, you can run tests against a staging or a production environment:"}),"\n",(0,s.jsxs)(i.A,{groupId:"bash-flavor",defaultValue:"bash",values:[{label:"Bash",value:"bash"},{label:"PowerShell",value:"powershell"},{label:"Batch",value:"batch"}],children:[(0,s.jsx)(l.A,{value:"bash",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"STAGING=1 npx playwright test\n"})})}),(0,s.jsx)(l.A,{value:"powershell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-powershell",children:"$env:STAGING=1\nnpx playwright test\n"})})}),(0,s.jsx)(l.A,{value:"batch",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:"set STAGING=1\nnpx playwright test\n"})})})]}),"\n",(0,s.jsx)(n.h3,{id:"env-files",children:".env files"}),"\n",(0,s.jsxs)(n.p,{children:["To make environment variables easier to manage, consider something like ",(0,s.jsx)(n.code,{children:".env"})," files. Here is an example that uses ",(0,s.jsx)(n.a,{href:"https://www.npmjs.com/package/dotenv",children:(0,s.jsx)(n.code,{children:"dotenv"})})," package to read environment variables directly in the configuration file."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="playwright.config.ts"',children:"import { defineConfig } from '@playwright/test';\nimport dotenv from 'dotenv';\nimport path from 'path';\n\n// Read from \".env\" file.\ndotenv.config({ path: path.resolve(__dirname, '.env') });\n\n// Alternatively, read from \"../my.env\" file.\ndotenv.config({ path: path.resolve(__dirname, '..', 'my.env') });\n\nexport default defineConfig({\n  use: {\n    baseURL: process.env.STAGING === '1' ? 'http://staging.example.test/' : 'http://example.test/',\n  }\n});\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Now, you can just edit ",(0,s.jsx)(n.code,{children:".env"})," file to set any variables you'd like."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# .env file\nSTAGING=0\nUSER_NAME=me\nPASSWORD=secret\n"})}),"\n",(0,s.jsx)(n.p,{children:"Run tests as usual, your environment variables should be picked up."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"npx playwright test\n"})}),"\n",(0,s.jsx)(n.h2,{id:"create-tests-via-a-csv-file",children:"Create tests via a CSV file"}),"\n",(0,s.jsx)(n.p,{children:"The Playwright test-runner runs in Node.js, this means you can directly read files from the file system and parse them with your preferred CSV library."}),"\n",(0,s.jsxs)(n.p,{children:["See for example this CSV file, in our example ",(0,s.jsx)(n.code,{children:"input.csv"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-txt",children:'"test_case","some_value","some_other_value"\n"value 1","value 11","foobar1"\n"value 2","value 22","foobar21"\n"value 3","value 33","foobar321"\n"value 4","value 44","foobar4321"\n'})}),"\n",(0,s.jsxs)(n.p,{children:["Based on this we'll generate some tests by using the ",(0,s.jsx)(n.a,{href:"https://www.npmjs.com/package/csv-parse",children:"csv-parse"})," library from NPM:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",metastring:'title="test.spec.ts"',children:"import fs from 'fs';\nimport path from 'path';\nimport { test } from '@playwright/test';\nimport { parse } from 'csv-parse/sync';\n\nconst records = parse(fs.readFileSync(path.join(__dirname, 'input.csv')), {\n  columns: true,\n  skip_empty_lines: true\n});\n\nfor (const record of records) {\n  test(`foo: ${record.test_case}`, async ({ page }) =>
1 {\n    console.log(record.test_case, record.some_value, record.some_other_value);\n  });\n}\n"})})]})}function m(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.