[{"data":1,"prerenderedAt":3946},["ShallowReactive",2],{"content:\u002Fsoftware-testing\u002Fframeworks\u002Fplaywright":3,"category:\u002Fsoftware-testing\u002Fframeworks\u002Fplaywright":56},{"id":4,"title":5,"bmcUsername":6,"body":7,"cover":6,"date":6,"description":47,"draft":48,"extension":49,"features":6,"githubRepo":6,"headline":6,"highlight":6,"icon":6,"meta":50,"navigation":51,"npmPackage":6,"order":6,"path":52,"seo":53,"stem":54,"__hash__":55},"content\u002Fsoftware-testing\u002Fframeworks\u002Fplaywright\u002Findex.md","Playwright",null,{"type":8,"value":9,"toc":43},"minimark",[10,14,17],[11,12,13],"p",{},"Practical guides for Playwright, Microsoft's open-source end-to-end testing framework. The focus is on the problems that show up once a Playwright suite grows past the hello-world stage: running tests in parallel without collisions, wiring them into CI, and stretching them into other kinds of testing. The guides draw on my experience testing real applications.",[11,15,16],{},"More Playwright articles from elsewhere on the site:",[18,19,20,29,36],"ul",{},[21,22,23,28],"li",{},[24,25,27],"a",{"href":26},"\u002Fsoftware-testing\u002Ftest-automation\u002Freuse-playwright-tests-for-load-testing","Reuse Your Playwright Tests for Load Testing"," covers driving load tests from functional tests you already maintain",[21,30,31,35],{},[24,32,34],{"href":33},"\u002Fsoftware-testing\u002Ftest-automation\u002Fplaywright-accessibility-testing-axe-lighthouse-limitations","Playwright Accessibility Testing: What axe and Lighthouse Miss"," covers writing smarter accessibility checks",[21,37,38,42],{},[24,39,41],{"href":40},"\u002Fsoftware-testing\u002Ftest-automation\u002Fstareast-2026-playwright-ai-cost-efficient-testing","Playwright AI Testing on a Budget: Locators vs. Computer Vision"," covers keeping AI-assisted Playwright testing affordable",{"title":44,"searchDepth":45,"depth":45,"links":46},"",2,[],"Hands-on Playwright guides from real test suites: parallel and serial test execution, load testing, accessibility testing, and AI-assisted testing.",false,"md",{},true,"\u002Fsoftware-testing\u002Fframeworks\u002Fplaywright",{"title":5,"description":47},"software-testing\u002Fframeworks\u002Fplaywright\u002Findex","CkWWcBRwe4OFC_YL5xyoKUqL4JFuPMlc1mtIsI_758Y",[57],{"id":58,"title":59,"bmcUsername":6,"body":60,"cover":3938,"date":3939,"description":3940,"draft":48,"extension":49,"features":6,"githubRepo":6,"headline":6,"highlight":6,"icon":6,"meta":3941,"navigation":51,"npmPackage":6,"order":6,"path":3942,"seo":3943,"stem":3944,"__hash__":3945},"content\u002Fsoftware-testing\u002Fframeworks\u002Fplaywright\u002Frun-parallel-and-serial-tests-in-playwright.md","Run Parallel and Serial Tests Together in Playwright",{"type":8,"value":61,"toc":3917},[62,65,68,76,79,82,87,120,123,132,136,147,350,361,722,725,729,732,761,776,782,791,795,802,816,1077,1091,1108,1132,1135,1140,1148,1281,1382,1393,1406,1410,1423,1516,1523,1695,1699,1712,1718,1725,1728,1910,1917,1920,1924,1933,2081,2084,2210,2220,2223,2278,2281,2289,2293,2296,2299,2302,2317,2840,2854,2895,2909,2915,3115,3121,3142,3146,3149,3152,3155,3190,3193,3197,3319,3325,3329,3332,3344,3348,3355,3493,3497,3509,3618,3624,3634,3862,3865,3871,3875,3881,3903,3909,3913],[11,63,64],{},"Modern test frameworks like Playwright allow you to run your tests in parallel instead of serially, allowing for faster feedback loops in your automated test runs. Serial test execution is many times slower than parallel runs because you are running tests one at a time. Every test you add slows your test pipeline down by the duration of that test. Parallel execution of your test automation lets you concurrently execute tests, where, with enough workers, your test pipeline would only be as slow as the longest-running test in the test suite.",[11,66,67],{},"This article covers a common problem that keeps test suites from running fully in parallel: tests that change global state to set up preconditions, which breaks other tests running at the same time that expect different preconditions.",[11,69,70,71,75],{},"This is what I am currently dealing with. Our test framework before Playwright had one big limitation: everything ran serially, or across several isolated test environments. Some tests changed global settings in ",[72,73,74],"code",{},"web.config",". Others alter global state by moving the server date to set up a precondition, like a billing rollover at month end. Run any of those next to another test and you break the other test. The entire legacy test suite was built around serial execution concepts that led to inefficient feedback loops that slowed down the Software Development Life Cycle (SDLC).",[11,77,78],{},"While modernizing and converting the legacy tests to Playwright, I was able to rework most scenarios to run in parallel without collisions or flakiness, but a few scenarios still required changing global state settings on the server, and they couldn't stay in the suite without making all the tests run serially.",[11,80,81],{},"Then I learned Playwright projects can separate parallel-safe tests from serial-only ones and run the serial ones on their own.",[83,84,86],"h2",{"id":85},"three-ways-to-run-serial-and-parallel-tests-together","Three Ways to Run Serial and Parallel Tests Together",[18,88,89,100,110],{},[21,90,91,99],{},[92,93,94,98],"strong",{},[24,95,97],{"href":96},"#option-a-playwright-project-dependencies-the-quick-win","Option A, project dependencies"," (the quick win):"," Split the tests into two projects and let Playwright run the serial one after the parallel one passes. It's a few lines of config and no CI changes, but there's a catch: one failing parallel test means the serial tests never run.",[21,101,102,109],{},[92,103,104,108],{},[24,105,107],{"href":106},"#option-b-separate-ci-steps-for-playwright-projects-fixing-the-catch","Option B, separate CI steps"," (fixes the catch):"," The same split, with each project run as its own CI step. It takes more wiring, but you get results from both suites on every run.",[21,111,112,119],{},[92,113,114,118],{},[24,115,117],{"href":116},"#option-c-preconfigured-test-environments-my-long-term-goal","Option C, preconfigured environments"," (my long-term goal):"," Stop changing settings from inside the tests. Stand up environments already configured for each precondition and point each test group at its own. It's the fastest and cleanest, but it isn't always feasible because of hosting cost and how easily you can create environments.",[11,121,122],{},"Options A and B split the tests the same way, by filename or by tag.",[11,124,125,126,131],{},"I built a small demo of every approach in this article. It's in the ",[127,128],"external-link",{"href":129,"text":130},"https:\u002F\u002Fgithub.com\u002Freallymello\u002Fplaywright-serial-parallel-dependencies-demo","playwright-serial-parallel-dependencies-demo"," repo, and the results below come from running it.",[83,133,135],{"id":134},"demoing-the-problem-playwright-tests-that-change-shared-state","Demoing the Problem: Playwright Tests That Change Shared State",[11,137,138,139,142,143,146],{},"The demo uses one shared JSON file as a stand-in for global server state, holding a feature flag and a \"server date\". The ",[72,140,141],{},"parallel-safe"," tests in the demo project only read it and expect the defaults. The ",[72,144,145],{},"serial-only"," tests change its state to set up a precondition, then reset it afterward:",[148,149,154],"pre",{"className":150,"code":151,"filename":152,"language":153,"meta":44,"style":44},"language-ts shiki shiki-themes material-theme-lighter github-light-high-contrast github-dark-high-contrast","test.afterEach(async () => {\n  await resetSettings();\n});\n\ntest('server date set to month end triggers billing rollover', async () => {\n  await writeSettings({ featureFlag: 'off', serverDate: '2026-01-31' });\n  expect((await readSettings()).serverDate).toBe('2026-01-31');\n});\n","tests\u002Fserver-settings.serial.spec.ts","ts",[72,155,156,189,205,216,222,251,299,341],{"__ignoreMap":44},[157,158,161,165,169,173,176,180,183,186],"span",{"class":159,"line":160},"line",1,[157,162,164],{"class":163},"sZ-rw","test",[157,166,168],{"class":167},"sPJuK",".",[157,170,172],{"class":171},"sb1SK","afterEach",[157,174,175],{"class":163},"(",[157,177,179],{"class":178},"stWsX","async",[157,181,182],{"class":167}," ()",[157,184,185],{"class":178}," =>",[157,187,188],{"class":167}," {\n",[157,190,191,195,198,202],{"class":159,"line":45},[157,192,194],{"class":193},"sZTni","  await",[157,196,197],{"class":171}," resetSettings",[157,199,201],{"class":200},"sq0XF","()",[157,203,204],{"class":167},";\n",[157,206,208,211,214],{"class":159,"line":207},3,[157,209,210],{"class":167},"}",[157,212,213],{"class":163},")",[157,215,204],{"class":167},[157,217,219],{"class":159,"line":218},4,[157,220,221],{"emptyLinePlaceholder":51},"\n",[157,223,225,227,229,233,237,239,242,245,247,249],{"class":159,"line":224},5,[157,226,164],{"class":171},[157,228,175],{"class":163},[157,230,232],{"class":231},"sZi47","'",[157,234,236],{"class":235},"srGNg","server date set to month end triggers billing rollover",[157,238,232],{"class":231},[157,240,241],{"class":167},",",[157,243,244],{"class":178}," async",[157,246,182],{"class":167},[157,248,185],{"class":178},[157,250,188],{"class":167},[157,252,254,256,259,261,264,267,270,273,276,278,280,283,285,287,290,292,295,297],{"class":159,"line":253},6,[157,255,194],{"class":193},[157,257,258],{"class":171}," writeSettings",[157,260,175],{"class":200},[157,262,263],{"class":167},"{",[157,265,266],{"class":200}," featureFlag",[157,268,269],{"class":167},":",[157,271,272],{"class":231}," '",[157,274,275],{"class":235},"off",[157,277,232],{"class":231},[157,279,241],{"class":167},[157,281,282],{"class":200}," serverDate",[157,284,269],{"class":167},[157,286,272],{"class":231},[157,288,289],{"class":235},"2026-01-31",[157,291,232],{"class":231},[157,293,294],{"class":167}," }",[157,296,213],{"class":200},[157,298,204],{"class":167},[157,300,302,305,308,311,314,317,319,322,324,326,329,331,333,335,337,339],{"class":159,"line":301},7,[157,303,304],{"class":171},"  expect",[157,306,307],{"class":200},"((",[157,309,310],{"class":193},"await",[157,312,313],{"class":171}," readSettings",[157,315,316],{"class":200},"())",[157,318,168],{"class":167},[157,320,321],{"class":163},"serverDate",[157,323,213],{"class":200},[157,325,168],{"class":167},[157,327,328],{"class":171},"toBe",[157,330,175],{"class":200},[157,332,232],{"class":231},[157,334,289],{"class":235},[157,336,232],{"class":231},[157,338,213],{"class":200},[157,340,204],{"class":167},[157,342,344,346,348],{"class":159,"line":343},8,[157,345,210],{"class":167},[157,347,213],{"class":163},[157,349,204],{"class":167},[11,351,352,353,356,357,360],{},"To see the shared state collision problem you can run ",[72,354,355],{},"npm run test:collision"," which evaluates to ",[72,358,359],{},"playwright test -c playwright.collision.config.ts",". The output below is trimmed to the first failure's details.",[148,362,367],{"className":363,"code":364,"filename":365,"language":366,"meta":44,"style":44},"language-ansi shiki shiki-themes material-theme-lighter github-light-high-contrast github-dark-high-contrast","npm run test:collision\n\n> playwright-serial-parallel-dependencies-demo@1.0.0 test:collision\n> playwright test -c playwright.collision.config.ts\n\n\n\u001b[2mRunning \u001b[22m11\u001b[2m tests using \u001b[22m5\u001b[2m workers\u001b[22m\n\n  \u001b[32m✓\u001b[39m  \u001b[2m 1 \u001b[22mtests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 4 uses default settings\u001b[2m (319ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 2 \u001b[22mtests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 1 uses default settings\u001b[2m (319ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 3 \u001b[22mtests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 3 uses default settings\u001b[2m (318ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 4 \u001b[22mtests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 2 uses default settings\u001b[2m (312ms)\u001b[22m\n  \u001b[32m-\u001b[39m  \u001b[2m 9 \u001b[22m\u001b[36mtests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1)\u001b[39m\n  \u001b[32m✓\u001b[39m  \u001b[2m 5 \u001b[22mtests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 1 renders with default settings\u001b[2m (311ms)\u001b[22m\n  \u001b[31m✘\u001b[39m  \u001b[2m 8 \u001b[22m\u001b[31mtests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings\u001b[39m\u001b[2m (305ms)\u001b[22m\n  \u001b[31m✘\u001b[39m  \u001b[2m 7 \u001b[22m\u001b[31mtests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings\u001b[39m\u001b[2m (305ms)\u001b[22m\n  \u001b[31m✘\u001b[39m  \u001b[2m 6 \u001b[22m\u001b[31mtests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings\u001b[39m\u001b[2m (307ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m10 \u001b[22mtests\\server-settings.serial.spec.ts:13:5 › feature flag on enables new checkout\u001b[2m (515ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m11 \u001b[22mtests\\server-settings.serial.spec.ts:19:5 › server date set to month end triggers billing rollover\u001b[2m (512ms)\u001b[22m\n\n\n\u001b[31m  1) tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings \u001b[2m──\u001b[22m\u001b[39m\n\n    Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) \u002F\u002F Object.is equality\u001b[22m\n\n    Expected: \u001b[32m\"o\u001b[7mff\u001b[27m\"\u001b[39m\n    Received: \u001b[31m\"o\u001b[7mn\u001b[27m\"\u001b[39m\n\n    ...\n\n\u001b[31m  3 failed\u001b[39m\n\u001b[31m    tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings \u001b[2m───\u001b[22m\u001b[39m\n\u001b[31m    tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings \u001b[2m───\u001b[22m\u001b[39m\n\u001b[31m    tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings \u001b[2m───\u001b[22m\u001b[39m\n\u001b[33m  1 skipped\u001b[39m\n\u001b[32m  7 passed\u001b[39m\u001b[2m (1.7s)\u001b[22m\n","Terminal","ansi",[72,368,369,375,379,384,389,393,397,415,419,435,448,462,476,489,503,519,532,546,560,574,579,584,594,599,624,629,645,659,664,670,675,681,690,698,706,713],{"__ignoreMap":44},[157,370,371],{"class":159,"line":160},[157,372,374],{"class":373},"sOsy2","npm run test:collision\n",[157,376,377],{"class":159,"line":45},[157,378,221],{"emptyLinePlaceholder":51},[157,380,381],{"class":159,"line":207},[157,382,383],{"class":373},"> playwright-serial-parallel-dependencies-demo@1.0.0 test:collision\n",[157,385,386],{"class":159,"line":218},[157,387,388],{"class":373},"> playwright test -c playwright.collision.config.ts\n",[157,390,391],{"class":159,"line":224},[157,392,221],{"emptyLinePlaceholder":51},[157,394,395],{"class":159,"line":253},[157,396,221],{"emptyLinePlaceholder":51},[157,398,399,403,406,409,412],{"class":159,"line":301},[157,400,402],{"class":401},"sFDEC","Running ",[157,404,405],{"class":373},"11",[157,407,408],{"class":401}," tests using ",[157,410,411],{"class":373},"5",[157,413,414],{"class":401}," workers\n",[157,416,417],{"class":159,"line":343},[157,418,221],{"emptyLinePlaceholder":51},[157,420,422,426,429,432],{"class":159,"line":421},9,[157,423,425],{"class":424},"ssIYW","  ✓",[157,427,428],{"class":401},"   1 ",[157,430,431],{"class":373},"tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 4 uses default settings",[157,433,434],{"class":401}," (319ms)\n",[157,436,438,440,443,446],{"class":159,"line":437},10,[157,439,425],{"class":424},[157,441,442],{"class":401},"   2 ",[157,444,445],{"class":373},"tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 1 uses default settings",[157,447,434],{"class":401},[157,449,451,453,456,459],{"class":159,"line":450},11,[157,452,425],{"class":424},[157,454,455],{"class":401},"   3 ",[157,457,458],{"class":373},"tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 3 uses default settings",[157,460,461],{"class":401}," (318ms)\n",[157,463,465,467,470,473],{"class":159,"line":464},12,[157,466,425],{"class":424},[157,468,469],{"class":401},"   4 ",[157,471,472],{"class":373},"tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 2 uses default settings",[157,474,475],{"class":401}," (312ms)\n",[157,477,479,482,485],{"class":159,"line":478},13,[157,480,481],{"class":424},"  -",[157,483,484],{"class":401},"   9 ",[157,486,488],{"class":487},"sCdEk","tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1)\n",[157,490,492,494,497,500],{"class":159,"line":491},14,[157,493,425],{"class":424},[157,495,496],{"class":401},"   5 ",[157,498,499],{"class":373},"tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 1 renders with default settings",[157,501,502],{"class":401}," (311ms)\n",[157,504,506,510,513,516],{"class":159,"line":505},15,[157,507,509],{"class":508},"sDbPz","  ✘",[157,511,512],{"class":401},"   8 ",[157,514,515],{"class":508},"tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings",[157,517,518],{"class":401}," (305ms)\n",[157,520,522,524,527,530],{"class":159,"line":521},16,[157,523,509],{"class":508},[157,525,526],{"class":401},"   7 ",[157,528,529],{"class":508},"tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings",[157,531,518],{"class":401},[157,533,535,537,540,543],{"class":159,"line":534},17,[157,536,509],{"class":508},[157,538,539],{"class":401},"   6 ",[157,541,542],{"class":508},"tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings",[157,544,545],{"class":401}," (307ms)\n",[157,547,549,551,554,557],{"class":159,"line":548},18,[157,550,425],{"class":424},[157,552,553],{"class":401},"  10 ",[157,555,556],{"class":373},"tests\\server-settings.serial.spec.ts:13:5 › feature flag on enables new checkout",[157,558,559],{"class":401}," (515ms)\n",[157,561,563,565,568,571],{"class":159,"line":562},19,[157,564,425],{"class":424},[157,566,567],{"class":401},"  11 ",[157,569,570],{"class":373},"tests\\server-settings.serial.spec.ts:19:5 › server date set to month end triggers billing rollover",[157,572,573],{"class":401}," (512ms)\n",[157,575,577],{"class":159,"line":576},20,[157,578,221],{"emptyLinePlaceholder":51},[157,580,582],{"class":159,"line":581},21,[157,583,221],{"emptyLinePlaceholder":51},[157,585,587,590],{"class":159,"line":586},22,[157,588,589],{"class":508},"  1) tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings ",[157,591,593],{"class":592},"smSMi","──\n",[157,595,597],{"class":159,"line":596},23,[157,598,221],{"emptyLinePlaceholder":51},[157,600,602,605,608,611,614,616,618,621],{"class":159,"line":601},24,[157,603,604],{"class":373},"    Error: ",[157,606,607],{"class":401},"expect(",[157,609,610],{"class":508},"received",[157,612,613],{"class":401},").",[157,615,328],{"class":373},[157,617,175],{"class":401},[157,619,620],{"class":424},"expected",[157,622,623],{"class":401},") \u002F\u002F Object.is equality\n",[157,625,627],{"class":159,"line":626},25,[157,628,221],{"emptyLinePlaceholder":51},[157,630,632,635,638,642],{"class":159,"line":631},26,[157,633,634],{"class":373},"    Expected: ",[157,636,637],{"class":424},"\"o",[157,639,641],{"class":640},"sBsy4","ff",[157,643,644],{"class":424},"\"\n",[157,646,648,651,653,657],{"class":159,"line":647},27,[157,649,650],{"class":373},"    Received: ",[157,652,637],{"class":508},[157,654,656],{"class":655},"sYBWh","n",[157,658,644],{"class":508},[157,660,662],{"class":159,"line":661},28,[157,663,221],{"emptyLinePlaceholder":51},[157,665,667],{"class":159,"line":666},29,[157,668,669],{"class":373},"    ...\n",[157,671,673],{"class":159,"line":672},30,[157,674,221],{"emptyLinePlaceholder":51},[157,676,678],{"class":159,"line":677},31,[157,679,680],{"class":508},"  3 failed\n",[157,682,684,687],{"class":159,"line":683},32,[157,685,686],{"class":508},"    tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings ",[157,688,689],{"class":592},"───\n",[157,691,693,696],{"class":159,"line":692},33,[157,694,695],{"class":508},"    tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings ",[157,697,689],{"class":592},[157,699,701,704],{"class":159,"line":700},34,[157,702,703],{"class":508},"    tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings ",[157,705,689],{"class":592},[157,707,709],{"class":159,"line":708},35,[157,710,712],{"class":711},"spoJ1","  1 skipped\n",[157,714,716,719],{"class":159,"line":715},36,[157,717,718],{"class":424},"  7 passed",[157,720,721],{"class":401}," (1.7s)\n",[11,723,724],{},"As shown above, running them together in a parallel pool introduces failures when the tests that expect the default state evaluate when the serial tests change the state before the teardown runs. The demo holds the changed state for half a second to make the collision likely, but a real backend process reacting to a changed date, feature flag, or web.config setting fails the same way, just less predictably.",[83,726,728],{"id":727},"serial-means-three-different-things-in-playwright","\"Serial\" Means Three Different Things in Playwright",[11,730,731],{},"Playwright has a few different ways of configuring serial runs depending on your use case and intention.",[18,733,734,742,755],{},[21,735,736,741],{},[92,737,738],{},[72,739,740],{},"workers: 1"," limits how many tests run at once. It says nothing about order, and one failure doesn't affect the other tests.",[21,743,744,749,750,754],{},[92,745,746],{},[72,747,748],{},"test.describe.configure({ mode: 'serial' })"," groups tests that depend on each other. ",[127,751],{"href":752,"text":753},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-parallel","The Playwright docs"," say: \"If one of the serial tests fails, all subsequent tests are skipped.\" That's right for a chain of dependent steps, and wrong for independent tests that each change some state.",[21,756,757,760],{},[92,758,759],{},"A serial project"," is a separate set of tests, selected by a matcher, that runs apart from the parallel-safe set. This is the one that solves the mixed-suite problem.",[11,762,763,764,766,767,771,772,775],{},"Even in parallel test runs many of my longer system testing user-journey type scenarios will be broken into individual tests in the same file that need to run serially, sequentially. For example, the first test may create an applicant and then the test after uses that applicant to apply for an account and so on. These test spec files use ",[72,765,748],{}," so that the tests in the spec run sequentially and skip tests that appear after the failing test ",[768,769,770],"em",{},"in that spec file",". So for my example, if the applicant creation test fails there is no point running the next test where that applicant is used to apply for an account. Playwright will still parallelize the spec files in that project while running tests ",[768,773,774],{},"within"," the spec files serially with that setting applied.",[11,777,778,779,781],{},"Using ",[72,780,740],{}," on the project will prevent Playwright from running the spec files in parallel as well.",[11,783,784,785,787,788,790],{},"Options A and B that we will cover in the following article sections put the state-changing tests in a ",[72,786,145],{}," project, and ",[72,789,740],{}," inside that project makes them run one at a time, serially.",[83,792,794],{"id":793},"split-tests-with-playwright-projects-and-matchers","Split Tests with Playwright Projects and Matchers",[11,796,797,801],{},[127,798],{"href":799,"text":800},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-projects","Playwright projects"," let you run different subsets of your tests with different settings. We will use this feature to determine which tests to run and when in the different solutions we will be showing to solve the parallel + serial test problem.",[11,803,804,805,808,809,812,813,269],{},"First, name the state-changing files ",[72,806,807],{},"*.serial.spec.ts"," and match on that using the ",[72,810,811],{},"projects"," setting in ",[72,814,815],{},"playwright.config.ts",[148,817,819],{"className":150,"code":818,"filename":815,"language":153,"meta":44,"style":44},"import { defineConfig } from '@playwright\u002Ftest';\n\nexport default defineConfig({\n  testDir: '.\u002Ftests',\n  projects: [\n    {\n      name: 'parallel-safe',\n      testMatch: \u002F.*\\.spec\\.ts\u002F,\n      testIgnore: \u002F.*\\.serial\\.spec\\.ts\u002F,\n      fullyParallel: true,\n    },\n    {\n      name: 'serial-only',\n      testMatch: \u002F.*\\.serial\\.spec\\.ts\u002F,\n      workers: 1, \u002F\u002F per-project workers requires Playwright 1.52+\n    },\n  ],\n});\n",[72,820,821,846,850,867,884,894,899,914,947,977,990,995,999,1013,1041,1058,1062,1069],{"__ignoreMap":44},[157,822,823,826,829,832,834,837,839,842,844],{"class":159,"line":160},[157,824,825],{"class":193},"import",[157,827,828],{"class":167}," {",[157,830,831],{"class":163}," defineConfig",[157,833,294],{"class":167},[157,835,836],{"class":193}," from",[157,838,272],{"class":231},[157,840,841],{"class":235},"@playwright\u002Ftest",[157,843,232],{"class":231},[157,845,204],{"class":167},[157,847,848],{"class":159,"line":45},[157,849,221],{"emptyLinePlaceholder":51},[157,851,852,855,858,860,863],{"class":159,"line":207},[157,853,854],{"class":193},"export",[157,856,857],{"class":193}," default",[157,859,831],{"class":171},[157,861,175],{"class":862},"sLHsM",[157,864,866],{"class":865},"sCRTB","{\n",[157,868,869,872,874,876,879,881],{"class":159,"line":218},[157,870,871],{"class":200},"  testDir",[157,873,269],{"class":167},[157,875,272],{"class":231},[157,877,878],{"class":235},".\u002Ftests",[157,880,232],{"class":231},[157,882,883],{"class":865},",\n",[157,885,886,889,891],{"class":159,"line":224},[157,887,888],{"class":200},"  projects",[157,890,269],{"class":167},[157,892,893],{"class":163}," [\n",[157,895,896],{"class":159,"line":253},[157,897,898],{"class":167},"    {\n",[157,900,901,904,906,908,910,912],{"class":159,"line":301},[157,902,903],{"class":200},"      name",[157,905,269],{"class":167},[157,907,272],{"class":231},[157,909,141],{"class":235},[157,911,232],{"class":231},[157,913,883],{"class":167},[157,915,916,919,921,924,927,931,935,938,940,942,945],{"class":159,"line":343},[157,917,918],{"class":200},"      testMatch",[157,920,269],{"class":167},[157,922,923],{"class":231}," \u002F",[157,925,168],{"class":926},"spMcu",[157,928,930],{"class":929},"sE6rD","*",[157,932,934],{"class":933},"szoTG","\\.",[157,936,937],{"class":235},"spec",[157,939,934],{"class":933},[157,941,153],{"class":235},[157,943,944],{"class":231},"\u002F",[157,946,883],{"class":167},[157,948,949,952,954,956,958,960,962,965,967,969,971,973,975],{"class":159,"line":421},[157,950,951],{"class":200},"      testIgnore",[157,953,269],{"class":167},[157,955,923],{"class":231},[157,957,168],{"class":926},[157,959,930],{"class":929},[157,961,934],{"class":933},[157,963,964],{"class":235},"serial",[157,966,934],{"class":933},[157,968,937],{"class":235},[157,970,934],{"class":933},[157,972,153],{"class":235},[157,974,944],{"class":231},[157,976,883],{"class":167},[157,978,979,982,984,988],{"class":159,"line":437},[157,980,981],{"class":200},"      fullyParallel",[157,983,269],{"class":167},[157,985,987],{"class":986},"sTqCK"," true",[157,989,883],{"class":167},[157,991,992],{"class":159,"line":450},[157,993,994],{"class":167},"    },\n",[157,996,997],{"class":159,"line":464},[157,998,898],{"class":167},[157,1000,1001,1003,1005,1007,1009,1011],{"class":159,"line":478},[157,1002,903],{"class":200},[157,1004,269],{"class":167},[157,1006,272],{"class":231},[157,1008,145],{"class":235},[157,1010,232],{"class":231},[157,1012,883],{"class":167},[157,1014,1015,1017,1019,1021,1023,1025,1027,1029,1031,1033,1035,1037,1039],{"class":159,"line":491},[157,1016,918],{"class":200},[157,1018,269],{"class":167},[157,1020,923],{"class":231},[157,1022,168],{"class":926},[157,1024,930],{"class":929},[157,1026,934],{"class":933},[157,1028,964],{"class":235},[157,1030,934],{"class":933},[157,1032,937],{"class":235},[157,1034,934],{"class":933},[157,1036,153],{"class":235},[157,1038,944],{"class":231},[157,1040,883],{"class":167},[157,1042,1043,1046,1048,1052,1054],{"class":159,"line":505},[157,1044,1045],{"class":200},"      workers",[157,1047,269],{"class":167},[157,1049,1051],{"class":1050},"s6g51"," 1",[157,1053,241],{"class":167},[157,1055,1057],{"class":1056},"s_gjE"," \u002F\u002F per-project workers requires Playwright 1.52+\n",[157,1059,1060],{"class":159,"line":521},[157,1061,994],{"class":167},[157,1063,1064,1067],{"class":159,"line":534},[157,1065,1066],{"class":163},"  ]",[157,1068,883],{"class":865},[157,1070,1071,1073,1075],{"class":159,"line":548},[157,1072,210],{"class":865},[157,1074,213],{"class":862},[157,1076,204],{"class":167},[11,1078,1079,1080,1083,1084,1086,1087,1090],{},"Note the use of ",[72,1081,1082],{},"testIgnore",". ",[72,1085,807],{}," also matches ",[72,1088,1089],{},".*\\.spec\\.ts",", so without it the serial files would land in both projects and run in parallel too.",[11,1092,1093,1094,1096,1097,1100,1101,1104,1105,168],{},"The ",[72,1095,141],{}," project will contain tests that ",[768,1098,1099],{},"play nicely with others",": they can run in parallel without colliding with other tests. There we set ",[72,1102,1103],{},"fullyParallel"," to ",[72,1106,1107],{},"true",[11,1109,1093,1110,1112,1113,1104,1116,1119,1120,1123,1124,1127,1128,1131],{},[72,1111,145],{}," project will hold the tests that change things that would create failures or flakiness in other tests if run at the same time. There we set the ",[72,1114,1115],{},"workers",[72,1117,1118],{},"1"," to ensure only one test runs at a time if they are in that project. These tests that ",[768,1121,1122],{},"don't"," play nicely with others will have the file suffix ",[72,1125,1126],{},".serial.spec.ts"," so we can use the ",[72,1129,1130],{},"testMatch"," parameter to find them.",[11,1133,1134],{},"The different solutions we will be going over use the project classifications in different ways to solve the parallel vs. serial testing problem.",[1136,1137,1139],"h3",{"id":1138},"match-on-tags-instead-of-filenames","Match on Tags Instead of Filenames",[11,1141,1142,1143,1147],{},"A filename convention forces you to keep state-changing tests in their own files. If you'd rather keep related tests together, tag the serial ones (see ",[127,1144],{"href":1145,"text":1146},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-annotations","Playwright test tags",") and match on the tag:",[148,1149,1152],{"className":150,"code":1150,"filename":1151,"language":153,"meta":44,"style":44},"test.describe('checkout with mutated server state', { tag: '@serial' }, () => {\n  test.describe.configure({ mode: 'serial' });\n\n  test('feature flag on enables new checkout', async () => {\n    \u002F\u002F ...\n  });\n});\n","tests-tagged\u002Fcheckout.spec.ts",[72,1153,1154,1197,1232,1236,1259,1264,1273],{"__ignoreMap":44},[157,1155,1156,1158,1160,1163,1165,1167,1170,1172,1174,1176,1179,1181,1183,1186,1188,1191,1193,1195],{"class":159,"line":160},[157,1157,164],{"class":163},[157,1159,168],{"class":167},[157,1161,1162],{"class":171},"describe",[157,1164,175],{"class":163},[157,1166,232],{"class":231},[157,1168,1169],{"class":235},"checkout with mutated server state",[157,1171,232],{"class":231},[157,1173,241],{"class":167},[157,1175,828],{"class":167},[157,1177,1178],{"class":200}," tag",[157,1180,269],{"class":167},[157,1182,272],{"class":231},[157,1184,1185],{"class":235},"@serial",[157,1187,232],{"class":231},[157,1189,1190],{"class":167}," },",[157,1192,182],{"class":167},[157,1194,185],{"class":178},[157,1196,188],{"class":167},[157,1198,1199,1202,1204,1206,1208,1211,1213,1215,1218,1220,1222,1224,1226,1228,1230],{"class":159,"line":45},[157,1200,1201],{"class":163},"  test",[157,1203,168],{"class":167},[157,1205,1162],{"class":163},[157,1207,168],{"class":167},[157,1209,1210],{"class":171},"configure",[157,1212,175],{"class":200},[157,1214,263],{"class":167},[157,1216,1217],{"class":200}," mode",[157,1219,269],{"class":167},[157,1221,272],{"class":231},[157,1223,964],{"class":235},[157,1225,232],{"class":231},[157,1227,294],{"class":167},[157,1229,213],{"class":200},[157,1231,204],{"class":167},[157,1233,1234],{"class":159,"line":207},[157,1235,221],{"emptyLinePlaceholder":51},[157,1237,1238,1240,1242,1244,1247,1249,1251,1253,1255,1257],{"class":159,"line":218},[157,1239,1201],{"class":171},[157,1241,175],{"class":200},[157,1243,232],{"class":231},[157,1245,1246],{"class":235},"feature flag on enables new checkout",[157,1248,232],{"class":231},[157,1250,241],{"class":167},[157,1252,244],{"class":178},[157,1254,182],{"class":167},[157,1256,185],{"class":178},[157,1258,188],{"class":167},[157,1260,1261],{"class":159,"line":224},[157,1262,1263],{"class":1056},"    \u002F\u002F ...\n",[157,1265,1266,1269,1271],{"class":159,"line":253},[157,1267,1268],{"class":167},"  }",[157,1270,213],{"class":200},[157,1272,204],{"class":167},[157,1274,1275,1277,1279],{"class":159,"line":301},[157,1276,210],{"class":167},[157,1278,213],{"class":163},[157,1280,204],{"class":167},[148,1282,1285],{"className":150,"code":1283,"filename":1284,"language":153,"meta":44,"style":44},"projects: [\n  { name: 'parallel-safe', grepInvert: \u002F@serial\u002F, fullyParallel: true },\n  { name: 'serial-only', grep: \u002F@serial\u002F, workers: 1 },\n],\n","playwright.tagged.config.ts",[72,1286,1287,1296,1337,1375],{"__ignoreMap":44},[157,1288,1289,1292,1294],{"class":159,"line":160},[157,1290,811],{"class":1291},"sA8fK",[157,1293,269],{"class":167},[157,1295,893],{"class":163},[157,1297,1298,1301,1304,1306,1308,1310,1312,1314,1317,1319,1321,1323,1325,1327,1330,1332,1334],{"class":159,"line":45},[157,1299,1300],{"class":167},"  {",[157,1302,1303],{"class":200}," name",[157,1305,269],{"class":167},[157,1307,272],{"class":231},[157,1309,141],{"class":235},[157,1311,232],{"class":231},[157,1313,241],{"class":167},[157,1315,1316],{"class":200}," grepInvert",[157,1318,269],{"class":167},[157,1320,923],{"class":231},[157,1322,1185],{"class":235},[157,1324,944],{"class":231},[157,1326,241],{"class":167},[157,1328,1329],{"class":200}," fullyParallel",[157,1331,269],{"class":167},[157,1333,987],{"class":986},[157,1335,1336],{"class":167}," },\n",[157,1338,1339,1341,1343,1345,1347,1349,1351,1353,1356,1358,1360,1362,1364,1366,1369,1371,1373],{"class":159,"line":207},[157,1340,1300],{"class":167},[157,1342,1303],{"class":200},[157,1344,269],{"class":167},[157,1346,272],{"class":231},[157,1348,145],{"class":235},[157,1350,232],{"class":231},[157,1352,241],{"class":167},[157,1354,1355],{"class":200}," grep",[157,1357,269],{"class":167},[157,1359,923],{"class":231},[157,1361,1185],{"class":235},[157,1363,944],{"class":231},[157,1365,241],{"class":167},[157,1367,1368],{"class":200}," workers",[157,1370,269],{"class":167},[157,1372,1051],{"class":1050},[157,1374,1336],{"class":167},[157,1376,1377,1380],{"class":159,"line":218},[157,1378,1379],{"class":163},"]",[157,1381,883],{"class":167},[11,1383,1384,1385,1388,1389,1392],{},"You can skip projects entirely and filter on the command line with ",[72,1386,1387],{},"--grep @serial"," and ",[72,1390,1391],{},"--grep-invert @serial",". The demo has that version too.",[11,1394,1395,1396,1399,1400,1402,1403,1405],{},"One trap: the demo keeps its tagged tests in a separate ",[72,1397,1398],{},"tests-tagged\u002F"," folder. A tagged file inside the folder your filename-based projects already scan would be picked up by the ",[72,1401,141],{}," project, and the ",[72,1404,1185],{}," tests would run in parallel anyway.",[83,1407,1409],{"id":1408},"option-a-playwright-project-dependencies-the-quick-win","Option A: Playwright Project Dependencies (The Quick Win)",[11,1411,1412,1413,1416,1417,1419,1420,1422],{},"Once the tests are split, you can use the Playwright ",[72,1414,1415],{},"dependencies"," project configuration setting to mark that the ",[72,1418,145],{}," project depends on the ",[72,1421,141],{}," project finishing first.",[148,1424,1427],{"className":150,"code":1425,"filename":1426,"language":153,"meta":44,"style":44},"{\n  name: 'serial-only',\n  testMatch: \u002F.*\\.serial\\.spec\\.ts\u002F,\n  dependencies: ['parallel-safe'], \u002F\u002F the magic happens here\n  workers: 1,\n},\n","playwright.dependencies.config.ts",[72,1428,1429,1433,1448,1477,1500,1511],{"__ignoreMap":44},[157,1430,1431],{"class":159,"line":160},[157,1432,866],{"class":167},[157,1434,1435,1438,1440,1442,1444,1446],{"class":159,"line":45},[157,1436,1437],{"class":1291},"  name",[157,1439,269],{"class":167},[157,1441,272],{"class":231},[157,1443,145],{"class":235},[157,1445,232],{"class":231},[157,1447,883],{"class":167},[157,1449,1450,1453,1455,1457,1459,1461,1463,1465,1467,1469,1471,1473,1475],{"class":159,"line":207},[157,1451,1452],{"class":1291},"  testMatch",[157,1454,269],{"class":167},[157,1456,923],{"class":231},[157,1458,168],{"class":926},[157,1460,930],{"class":929},[157,1462,934],{"class":933},[157,1464,964],{"class":235},[157,1466,934],{"class":933},[157,1468,937],{"class":235},[157,1470,934],{"class":933},[157,1472,153],{"class":235},[157,1474,944],{"class":231},[157,1476,883],{"class":167},[157,1478,1479,1482,1484,1487,1489,1491,1493,1495,1497],{"class":159,"line":218},[157,1480,1481],{"class":1291},"  dependencies",[157,1483,269],{"class":167},[157,1485,1486],{"class":200}," [",[157,1488,232],{"class":231},[157,1490,141],{"class":235},[157,1492,232],{"class":231},[157,1494,1379],{"class":200},[157,1496,241],{"class":167},[157,1498,1499],{"class":1056}," \u002F\u002F the magic happens here\n",[157,1501,1502,1505,1507,1509],{"class":159,"line":224},[157,1503,1504],{"class":1291},"  workers",[157,1506,269],{"class":167},[157,1508,1051],{"class":1050},[157,1510,883],{"class":167},[157,1512,1513],{"class":159,"line":253},[157,1514,1515],{"class":167},"},\n",[11,1517,1518,1519,1522],{},"This is a quick and easy way to solve the problem. The parallel tests run first, and the serial ones run after they finish, so nothing collides. In the demo project, running ",[72,1520,1521],{},"npm run test:deps"," will run with this configuration-based solution.",[148,1524,1526],{"className":363,"code":1525,"filename":365,"language":366,"meta":44,"style":44},"npx playwright test -c playwright.dependencies.config.ts\n\n\u001b[2mRunning \u001b[22m11\u001b[2m tests using \u001b[22m9\u001b[2m workers\u001b[22m\n\n  \u001b[32m-\u001b[39m  \u001b[2m 9 \u001b[22m\u001b[36m[parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1)\u001b[39m\n  \u001b[32m✓\u001b[39m  \u001b[2m 1 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 2 uses default settings\u001b[2m (309ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 2 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 4 uses default settings\u001b[2m (320ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 4 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 1 uses default settings\u001b[2m (307ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 3 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 3 uses default settings\u001b[2m (316ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 5 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 1 renders with default settings\u001b[2m (319ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 6 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings\u001b[2m (313ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 7 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings\u001b[2m (311ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 8 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings\u001b[2m (319ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m10 \u001b[22m[serial-only] › tests\\server-settings.serial.spec.ts:13:5 › feature flag on enables new checkout\u001b[2m (517ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m11 \u001b[22m[serial-only] › tests\\server-settings.serial.spec.ts:19:5 › server date set to month end triggers billing rollover\u001b[2m (508ms)\u001b[22m\n\n\u001b[33m  1 skipped\u001b[39m\n\u001b[32m  10 passed\u001b[39m\u001b[2m (1.9s)\u001b[22m\n",[72,1527,1528,1533,1537,1550,1554,1563,1575,1587,1598,1610,1621,1633,1644,1655,1667,1679,1683,1687],{"__ignoreMap":44},[157,1529,1530],{"class":159,"line":160},[157,1531,1532],{"class":373},"npx playwright test -c playwright.dependencies.config.ts\n",[157,1534,1535],{"class":159,"line":45},[157,1536,221],{"emptyLinePlaceholder":51},[157,1538,1539,1541,1543,1545,1548],{"class":159,"line":207},[157,1540,402],{"class":401},[157,1542,405],{"class":373},[157,1544,408],{"class":401},[157,1546,1547],{"class":373},"9",[157,1549,414],{"class":401},[157,1551,1552],{"class":159,"line":218},[157,1553,221],{"emptyLinePlaceholder":51},[157,1555,1556,1558,1560],{"class":159,"line":224},[157,1557,481],{"class":424},[157,1559,484],{"class":401},[157,1561,1562],{"class":487},"[parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1)\n",[157,1564,1565,1567,1569,1572],{"class":159,"line":253},[157,1566,425],{"class":424},[157,1568,428],{"class":401},[157,1570,1571],{"class":373},"[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 2 uses default settings",[157,1573,1574],{"class":401}," (309ms)\n",[157,1576,1577,1579,1581,1584],{"class":159,"line":301},[157,1578,425],{"class":424},[157,1580,442],{"class":401},[157,1582,1583],{"class":373},"[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 4 uses default settings",[157,1585,1586],{"class":401}," (320ms)\n",[157,1588,1589,1591,1593,1596],{"class":159,"line":343},[157,1590,425],{"class":424},[157,1592,469],{"class":401},[157,1594,1595],{"class":373},"[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 1 uses default settings",[157,1597,545],{"class":401},[157,1599,1600,1602,1604,1607],{"class":159,"line":421},[157,1601,425],{"class":424},[157,1603,455],{"class":401},[157,1605,1606],{"class":373},"[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 3 uses default settings",[157,1608,1609],{"class":401}," (316ms)\n",[157,1611,1612,1614,1616,1619],{"class":159,"line":437},[157,1613,425],{"class":424},[157,1615,496],{"class":401},[157,1617,1618],{"class":373},"[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 1 renders with default settings",[157,1620,434],{"class":401},[157,1622,1623,1625,1627,1630],{"class":159,"line":450},[157,1624,425],{"class":424},[157,1626,539],{"class":401},[157,1628,1629],{"class":373},"[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings",[157,1631,1632],{"class":401}," (313ms)\n",[157,1634,1635,1637,1639,1642],{"class":159,"line":464},[157,1636,425],{"class":424},[157,1638,526],{"class":401},[157,1640,1641],{"class":373},"[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings",[157,1643,502],{"class":401},[157,1645,1646,1648,1650,1653],{"class":159,"line":478},[157,1647,425],{"class":424},[157,1649,512],{"class":401},[157,1651,1652],{"class":373},"[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings",[157,1654,434],{"class":401},[157,1656,1657,1659,1661,1664],{"class":159,"line":491},[157,1658,425],{"class":424},[157,1660,553],{"class":401},[157,1662,1663],{"class":373},"[serial-only] › tests\\server-settings.serial.spec.ts:13:5 › feature flag on enables new checkout",[157,1665,1666],{"class":401}," (517ms)\n",[157,1668,1669,1671,1673,1676],{"class":159,"line":505},[157,1670,425],{"class":424},[157,1672,567],{"class":401},[157,1674,1675],{"class":373},"[serial-only] › tests\\server-settings.serial.spec.ts:19:5 › server date set to month end triggers billing rollover",[157,1677,1678],{"class":401}," (508ms)\n",[157,1680,1681],{"class":159,"line":521},[157,1682,221],{"emptyLinePlaceholder":51},[157,1684,1685],{"class":159,"line":534},[157,1686,712],{"class":711},[157,1688,1689,1692],{"class":159,"line":548},[157,1690,1691],{"class":424},"  10 passed",[157,1693,1694],{"class":401}," (1.9s)\n",[1136,1696,1698],{"id":1697},"the-gotcha-a-failing-test-skips-the-serial-suite","The Gotcha: A Failing Test Skips the Serial Suite",[11,1700,1701,1702,1705,1706,1708,1709,269],{},"There is a ",[768,1703,1704],{},"gotcha"," or trade-off to leveraging the ",[72,1707,1415],{}," setting to separate and run the serial tests after the parallel tests finish. Per the ",[127,1710],{"href":799,"text":1711},"Playwright documentation",[1713,1714,1715],"blockquote",{},[11,1716,1717],{},"\"If the tests from a dependency fails then the tests that rely on this project will not be run.\"",[11,1719,1720,1721,1724],{},"So if there is a test failure in the parallel tests the serial tests ",[768,1722,1723],{},"will not run"," so you'd lose visibility and coverage for that run for your serial tests since they are skipped.",[11,1726,1727],{},"The demo project uses this DEMO_FAIL test that has been getting skipped in the earlier runs to show this downside in action.",[148,1729,1731],{"className":363,"code":1730,"filename":365,"language":366,"meta":44,"style":44},"DEMO_FAIL=1 npx playwright test -c playwright.dependencies.config.ts\n\n\u001b[2mRunning \u001b[22m11\u001b[2m tests using \u001b[22m9\u001b[2m workers\u001b[22m\n\n  \u001b[31m✘\u001b[39m  \u001b[2m 9 \u001b[22m\u001b[31m[parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1)\u001b[39m\u001b[2m (4ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 3 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 4 renders with default settings\u001b[2m (305ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 1 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 2 uses default settings\u001b[2m (312ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 5 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 3 uses default settings\u001b[2m (317ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 4 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 4 uses default settings\u001b[2m (316ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 2 \u001b[22m[parallel-safe] › tests\\pricing.spec.ts:8:9 › pricing (parallel-safe) › quote 1 uses default settings\u001b[2m (322ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 7 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 2 renders with default settings\u001b[2m (310ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 8 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 3 renders with default settings\u001b[2m (310ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 6 \u001b[22m[parallel-safe] › tests\\reports.spec.ts:6:9 › reports (parallel-safe) › report 1 renders with default settings\u001b[2m (316ms)\u001b[22m\n\n\n\u001b[31m  1) [parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1) \u001b[39m\n\n    Error: forced failure to demonstrate dependency blocking\n\n    ...\n\n\u001b[31m  1 failed\u001b[39m\n\u001b[31m    [parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1) \u001b[39m\n\u001b[33m  2 did not run\u001b[39m\n\u001b[32m  8 passed\u001b[39m\u001b[2m (656ms)\u001b[22m\n",[72,1732,1733,1738,1742,1754,1758,1770,1780,1790,1801,1811,1822,1833,1843,1853,1857,1861,1866,1870,1875,1879,1883,1887,1892,1897,1902],{"__ignoreMap":44},[157,1734,1735],{"class":159,"line":160},[157,1736,1737],{"class":373},"DEMO_FAIL=1 npx playwright test -c playwright.dependencies.config.ts\n",[157,1739,1740],{"class":159,"line":45},[157,1741,221],{"emptyLinePlaceholder":51},[157,1743,1744,1746,1748,1750,1752],{"class":159,"line":207},[157,1745,402],{"class":401},[157,1747,405],{"class":373},[157,1749,408],{"class":401},[157,1751,1547],{"class":373},[157,1753,414],{"class":401},[157,1755,1756],{"class":159,"line":218},[157,1757,221],{"emptyLinePlaceholder":51},[157,1759,1760,1762,1764,1767],{"class":159,"line":224},[157,1761,509],{"class":508},[157,1763,484],{"class":401},[157,1765,1766],{"class":508},"[parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1)",[157,1768,1769],{"class":401}," (4ms)\n",[157,1771,1772,1774,1776,1778],{"class":159,"line":253},[157,1773,425],{"class":424},[157,1775,455],{"class":401},[157,1777,1641],{"class":373},[157,1779,518],{"class":401},[157,1781,1782,1784,1786,1788],{"class":159,"line":301},[157,1783,425],{"class":424},[157,1785,428],{"class":401},[157,1787,1571],{"class":373},[157,1789,475],{"class":401},[157,1791,1792,1794,1796,1798],{"class":159,"line":343},[157,1793,425],{"class":424},[157,1795,496],{"class":401},[157,1797,1606],{"class":373},[157,1799,1800],{"class":401}," (317ms)\n",[157,1802,1803,1805,1807,1809],{"class":159,"line":421},[157,1804,425],{"class":424},[157,1806,469],{"class":401},[157,1808,1583],{"class":373},[157,1810,1609],{"class":401},[157,1812,1813,1815,1817,1819],{"class":159,"line":437},[157,1814,425],{"class":424},[157,1816,442],{"class":401},[157,1818,1595],{"class":373},[157,1820,1821],{"class":401}," (322ms)\n",[157,1823,1824,1826,1828,1830],{"class":159,"line":450},[157,1825,425],{"class":424},[157,1827,526],{"class":401},[157,1829,1629],{"class":373},[157,1831,1832],{"class":401}," (310ms)\n",[157,1834,1835,1837,1839,1841],{"class":159,"line":464},[157,1836,425],{"class":424},[157,1838,512],{"class":401},[157,1840,1652],{"class":373},[157,1842,1832],{"class":401},[157,1844,1845,1847,1849,1851],{"class":159,"line":478},[157,1846,425],{"class":424},[157,1848,539],{"class":401},[157,1850,1618],{"class":373},[157,1852,1609],{"class":401},[157,1854,1855],{"class":159,"line":491},[157,1856,221],{"emptyLinePlaceholder":51},[157,1858,1859],{"class":159,"line":505},[157,1860,221],{"emptyLinePlaceholder":51},[157,1862,1863],{"class":159,"line":521},[157,1864,1865],{"class":508},"  1) [parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1) \n",[157,1867,1868],{"class":159,"line":534},[157,1869,221],{"emptyLinePlaceholder":51},[157,1871,1872],{"class":159,"line":548},[157,1873,1874],{"class":373},"    Error: forced failure to demonstrate dependency blocking\n",[157,1876,1877],{"class":159,"line":562},[157,1878,221],{"emptyLinePlaceholder":51},[157,1880,1881],{"class":159,"line":576},[157,1882,669],{"class":373},[157,1884,1885],{"class":159,"line":581},[157,1886,221],{"emptyLinePlaceholder":51},[157,1888,1889],{"class":159,"line":586},[157,1890,1891],{"class":508},"  1 failed\n",[157,1893,1894],{"class":159,"line":596},[157,1895,1896],{"class":508},"    [parallel-safe] › tests\\reports.spec.ts:15:7 › reports (parallel-safe) › deliberately failing test (only when DEMO_FAIL=1) \n",[157,1898,1899],{"class":159,"line":601},[157,1900,1901],{"class":711},"  2 did not run\n",[157,1903,1904,1907],{"class":159,"line":626},[157,1905,1906],{"class":424},"  8 passed",[157,1908,1909],{"class":401}," (656ms)\n",[11,1911,1912,1913,1916],{},"The two serial tests never ran as indicated by ",[72,1914,1915],{},"2 did not run"," in the terminal output. In a real suite, it means one failing test in a large parallel-safe project hides the results of the entire serial project. It also changes what \"the suite ran\" means when someone triages a red build. Every failure has to be fixed before you can see whether the second half is healthy at all. In other words, you don't know what other issues are hiding until you fix the early test failures which can make troubleshooting frustrating if you fix one test, rerun, and find another issue that was hiding behind it.",[11,1918,1919],{},"For my suite that's a dealbreaker. The serial tests cover some of the more important financial transactions in the systems that are part of the critical path. I want to know about a failure there on the same run where a parallel test fails, not one fix cycle later.",[83,1921,1923],{"id":1922},"option-b-separate-ci-steps-for-playwright-projects-fixing-the-catch","Option B: Separate CI Steps for Playwright Projects (Fixing the Catch)",[11,1925,1926,1927,1929,1930,1932],{},"As mentioned, Option A, leveraging Playwright ",[72,1928,1415],{},", is an easy solution to keep the serial and parallel tests separated, but has the trade-off where an upstream failure in the parallel test project causes your serial tests to be skipped. The fix in Option B is to keep leveraging Playwright projects to subset the parallel and serial tests, but drop ",[72,1931,1415],{}," and let CI do the ordering. The projects stay in the config, but each gets its own invocation:",[148,1934,1936],{"className":150,"code":1935,"filename":815,"language":153,"meta":44,"style":44},"projects: [\n  { name: 'parallel-safe', testMatch: \u002F.*\\.spec\\.ts\u002F, testIgnore: \u002F.*\\.serial\\.spec\\.ts\u002F, fullyParallel: true },\n  { name: 'serial-only', testMatch: \u002F.*\\.serial\\.spec\\.ts\u002F, workers: 1 },\n  \u002F\u002F no `dependencies` between them\n],\n",[72,1937,1938,1946,2020,2070,2075],{"__ignoreMap":44},[157,1939,1940,1942,1944],{"class":159,"line":160},[157,1941,811],{"class":1291},[157,1943,269],{"class":167},[157,1945,893],{"class":163},[157,1947,1948,1950,1952,1954,1956,1958,1960,1962,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1988,1990,1992,1994,1996,1998,2000,2002,2004,2006,2008,2010,2012,2014,2016,2018],{"class":159,"line":45},[157,1949,1300],{"class":167},[157,1951,1303],{"class":200},[157,1953,269],{"class":167},[157,1955,272],{"class":231},[157,1957,141],{"class":235},[157,1959,232],{"class":231},[157,1961,241],{"class":167},[157,1963,1964],{"class":200}," testMatch",[157,1966,269],{"class":167},[157,1968,923],{"class":231},[157,1970,168],{"class":926},[157,1972,930],{"class":929},[157,1974,934],{"class":933},[157,1976,937],{"class":235},[157,1978,934],{"class":933},[157,1980,153],{"class":235},[157,1982,944],{"class":231},[157,1984,241],{"class":167},[157,1986,1987],{"class":200}," testIgnore",[157,1989,269],{"class":167},[157,1991,923],{"class":231},[157,1993,168],{"class":926},[157,1995,930],{"class":929},[157,1997,934],{"class":933},[157,1999,964],{"class":235},[157,2001,934],{"class":933},[157,2003,937],{"class":235},[157,2005,934],{"class":933},[157,2007,153],{"class":235},[157,2009,944],{"class":231},[157,2011,241],{"class":167},[157,2013,1329],{"class":200},[157,2015,269],{"class":167},[157,2017,987],{"class":986},[157,2019,1336],{"class":167},[157,2021,2022,2024,2026,2028,2030,2032,2034,2036,2038,2040,2042,2044,2046,2048,2050,2052,2054,2056,2058,2060,2062,2064,2066,2068],{"class":159,"line":207},[157,2023,1300],{"class":167},[157,2025,1303],{"class":200},[157,2027,269],{"class":167},[157,2029,272],{"class":231},[157,2031,145],{"class":235},[157,2033,232],{"class":231},[157,2035,241],{"class":167},[157,2037,1964],{"class":200},[157,2039,269],{"class":167},[157,2041,923],{"class":231},[157,2043,168],{"class":926},[157,2045,930],{"class":929},[157,2047,934],{"class":933},[157,2049,964],{"class":235},[157,2051,934],{"class":933},[157,2053,937],{"class":235},[157,2055,934],{"class":933},[157,2057,153],{"class":235},[157,2059,944],{"class":231},[157,2061,241],{"class":167},[157,2063,1368],{"class":200},[157,2065,269],{"class":167},[157,2067,1051],{"class":1050},[157,2069,1336],{"class":167},[157,2071,2072],{"class":159,"line":218},[157,2073,2074],{"class":1056},"  \u002F\u002F no `dependencies` between them\n",[157,2076,2077,2079],{"class":159,"line":224},[157,2078,1379],{"class":163},[157,2080,883],{"class":167},[11,2082,2083],{},"In GitHub Actions, run the parallel-safe step first, let the serial step run whether or not the first one failed, then fail the job if either did:",[148,2085,2090],{"className":2086,"code":2087,"filename":2088,"language":2089,"meta":44,"style":44},"language-yaml shiki shiki-themes material-theme-lighter github-light-high-contrast github-dark-high-contrast","- name: Parallel-safe tests\n  id: parallel\n  run: npx playwright test --project=parallel-safe\n  continue-on-error: true\n\n- name: Serial-only tests\n  id: serial\n  run: npx playwright test --project=serial-only --workers=1\n  continue-on-error: true\n\n- name: Fail job if either suite failed\n  if: steps.parallel.outcome == 'failure' || steps.serial.outcome == 'failure'\n  run: exit 1\n",".github\u002Fworkflows\u002Fsplit-steps.yml","yaml",[72,2091,2092,2105,2115,2125,2135,2139,2150,2159,2168,2176,2180,2191,2201],{"__ignoreMap":44},[157,2093,2094,2097,2100,2102],{"class":159,"line":160},[157,2095,2096],{"class":167},"-",[157,2098,1303],{"class":2099},"saWzx",[157,2101,269],{"class":167},[157,2103,2104],{"class":235}," Parallel-safe tests\n",[157,2106,2107,2110,2112],{"class":159,"line":45},[157,2108,2109],{"class":2099},"  id",[157,2111,269],{"class":167},[157,2113,2114],{"class":235}," parallel\n",[157,2116,2117,2120,2122],{"class":159,"line":207},[157,2118,2119],{"class":2099},"  run",[157,2121,269],{"class":167},[157,2123,2124],{"class":235}," npx playwright test --project=parallel-safe\n",[157,2126,2127,2130,2132],{"class":159,"line":218},[157,2128,2129],{"class":2099},"  continue-on-error",[157,2131,269],{"class":167},[157,2133,2134],{"class":986}," true\n",[157,2136,2137],{"class":159,"line":224},[157,2138,221],{"emptyLinePlaceholder":51},[157,2140,2141,2143,2145,2147],{"class":159,"line":253},[157,2142,2096],{"class":167},[157,2144,1303],{"class":2099},[157,2146,269],{"class":167},[157,2148,2149],{"class":235}," Serial-only tests\n",[157,2151,2152,2154,2156],{"class":159,"line":301},[157,2153,2109],{"class":2099},[157,2155,269],{"class":167},[157,2157,2158],{"class":235}," serial\n",[157,2160,2161,2163,2165],{"class":159,"line":343},[157,2162,2119],{"class":2099},[157,2164,269],{"class":167},[157,2166,2167],{"class":235}," npx playwright test --project=serial-only --workers=1\n",[157,2169,2170,2172,2174],{"class":159,"line":421},[157,2171,2129],{"class":2099},[157,2173,269],{"class":167},[157,2175,2134],{"class":986},[157,2177,2178],{"class":159,"line":437},[157,2179,221],{"emptyLinePlaceholder":51},[157,2181,2182,2184,2186,2188],{"class":159,"line":450},[157,2183,2096],{"class":167},[157,2185,1303],{"class":2099},[157,2187,269],{"class":167},[157,2189,2190],{"class":235}," Fail job if either suite failed\n",[157,2192,2193,2196,2198],{"class":159,"line":464},[157,2194,2195],{"class":2099},"  if",[157,2197,269],{"class":167},[157,2199,2200],{"class":235}," steps.parallel.outcome == 'failure' || steps.serial.outcome == 'failure'\n",[157,2202,2203,2205,2207],{"class":159,"line":478},[157,2204,2119],{"class":2099},[157,2206,269],{"class":167},[157,2208,2209],{"class":235}," exit 1\n",[11,2211,2212,2215,2216,2219],{},[72,2213,2214],{},"continue-on-error: true"," keeps a failed step from stopping the job, and the ",[72,2217,2218],{},"outcome"," check in the last step reads the result of each step before that setting is applied. The last step is what turns the build red.",[11,2221,2222],{},"Run the same forced failure locally as two separate commands and you get the result the dependency version couldn't:",[148,2224,2228],{"className":2225,"code":2226,"filename":365,"language":2227,"meta":44,"style":44},"language-zsh shiki shiki-themes material-theme-lighter github-light-high-contrast github-dark-high-contrast","DEMO_FAIL=1 npx playwright test --project=parallel-safe\n# 1 failed, 8 passed\n\nnpx playwright test --project=serial-only\n# 2 passed\n","zsh",[72,2229,2230,2252,2257,2261,2273],{"__ignoreMap":44},[157,2231,2232,2235,2238,2240,2243,2246,2249],{"class":159,"line":160},[157,2233,2234],{"class":163},"DEMO_FAIL",[157,2236,2237],{"class":929},"=",[157,2239,1118],{"class":235},[157,2241,2242],{"class":1291}," npx",[157,2244,2245],{"class":235}," playwright",[157,2247,2248],{"class":235}," test",[157,2250,2251],{"class":926}," --project=parallel-safe\n",[157,2253,2254],{"class":159,"line":45},[157,2255,2256],{"class":1056},"# 1 failed, 8 passed\n",[157,2258,2259],{"class":159,"line":207},[157,2260,221],{"emptyLinePlaceholder":51},[157,2262,2263,2266,2268,2270],{"class":159,"line":218},[157,2264,2265],{"class":1291},"npx",[157,2267,2245],{"class":235},[157,2269,2248],{"class":235},[157,2271,2272],{"class":926}," --project=serial-only\n",[157,2274,2275],{"class":159,"line":224},[157,2276,2277],{"class":1056},"# 2 passed\n",[11,2279,2280],{},"The parallel step fails and the serial step still runs and passes. The order is still preserved, because the serial tests only start after the parallel step finishes. The two suites just no longer depend on each other's results with no coverage compromise this time.",[11,2282,2283,2284,1388,2286,168],{},"The tag version works with the same two steps. Change the commands to ",[72,2285,1391],{},[72,2287,2288],{},"--grep @serial --workers=1",[83,2290,2292],{"id":2291},"option-c-preconfigured-test-environments-my-long-term-goal","Option C: Preconfigured Test Environments (My Long-Term Goal)",[11,2294,2295],{},"Both options so far keep the underlying problem. The tests still change global state on the fly, so they still need the environment to themselves. That covers things like the server date, web server settings, language settings, and feature flags.",[11,2297,2298],{},"What I'd rather do is stand up test environments that are already configured with those settings. It's less invasive and tends to be less of a flaky pattern than changing major server settings mid-test. It also lessens the amount of tests in the serial project grouping if groups of tests with similar state settings can run together on isolated environments.",[11,2300,2301],{},"One environment would have its server date set to a month end. One has its language set to Spanish. One has the new-checkout feature flag on. Tests that need a given precondition run against the environment that already has it, so nothing has to change a setting mid-run. That lets each group of tests run isolated from the rest and in parallel with them, with no ordering step in CI at all.",[11,2303,2304,2305,2308,2309,2312,2313,2316],{},"Playwright supports this with per-project ",[72,2306,2307],{},"use"," options. ",[127,2310],{"href":2311,"text":753},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-use-options"," say you can override options for a specific project, and ",[72,2314,2315],{},"baseURL"," is one of them. Name the test files after the environment they need, then match on that naming convention:",[148,2318,2320],{"className":150,"code":2319,"filename":815,"language":153,"meta":44,"style":44},"import { defineConfig } from '@playwright\u002Ftest';\nimport dotenv from 'dotenv';\nimport path from 'path';\n\ndotenv.config({ path: path.resolve(__dirname, '.env') });\n\nexport default defineConfig({\n  testDir: '.\u002Ftests',\n  fullyParallel: true,\n  projects: [\n    {\n      name: 'parallel-safe',\n      testMatch: \u002F.*\\.spec\\.ts\u002F,\n      testIgnore: \u002F.*\\.(month-end|spanish|new-checkout)\\.spec\\.ts\u002F,\n      use: { baseURL: process.env.DEFAULT_BASE_URL },\n    },\n    {\n      name: 'month-end-date',\n      testMatch: \u002F.*\\.month-end\\.spec\\.ts\u002F,\n      use: { baseURL: process.env.MONTH_END_BASE_URL },\n    },\n    {\n      name: 'spanish-locale',\n      testMatch: \u002F.*\\.spanish\\.spec\\.ts\u002F,\n      use: { baseURL: process.env.SPANISH_BASE_URL },\n    },\n    {\n      name: 'new-checkout-flag',\n      testMatch: \u002F.*\\.new-checkout\\.spec\\.ts\u002F,\n      use: { baseURL: process.env.NEW_CHECKOUT_BASE_URL },\n    },\n  ],\n});\n",[72,2321,2322,2342,2361,2379,2383,2429,2433,2445,2459,2470,2478,2482,2496,2520,2564,2594,2598,2602,2617,2645,2670,2674,2678,2693,2721,2746,2750,2754,2769,2797,2822,2826,2832],{"__ignoreMap":44},[157,2323,2324,2326,2328,2330,2332,2334,2336,2338,2340],{"class":159,"line":160},[157,2325,825],{"class":193},[157,2327,828],{"class":167},[157,2329,831],{"class":163},[157,2331,294],{"class":167},[157,2333,836],{"class":193},[157,2335,272],{"class":231},[157,2337,841],{"class":235},[157,2339,232],{"class":231},[157,2341,204],{"class":167},[157,2343,2344,2346,2349,2352,2354,2357,2359],{"class":159,"line":45},[157,2345,825],{"class":193},[157,2347,2348],{"class":163}," dotenv ",[157,2350,2351],{"class":193},"from",[157,2353,272],{"class":231},[157,2355,2356],{"class":235},"dotenv",[157,2358,232],{"class":231},[157,2360,204],{"class":167},[157,2362,2363,2365,2368,2370,2372,2375,2377],{"class":159,"line":207},[157,2364,825],{"class":193},[157,2366,2367],{"class":163}," path ",[157,2369,2351],{"class":193},[157,2371,272],{"class":231},[157,2373,2374],{"class":235},"path",[157,2376,232],{"class":231},[157,2378,204],{"class":167},[157,2380,2381],{"class":159,"line":218},[157,2382,221],{"emptyLinePlaceholder":51},[157,2384,2385,2387,2389,2392,2394,2396,2399,2401,2403,2405,2408,2411,2413,2415,2418,2420,2423,2425,2427],{"class":159,"line":224},[157,2386,2356],{"class":163},[157,2388,168],{"class":167},[157,2390,2391],{"class":171},"config",[157,2393,175],{"class":163},[157,2395,263],{"class":167},[157,2397,2398],{"class":200}," path",[157,2400,269],{"class":167},[157,2402,2398],{"class":163},[157,2404,168],{"class":167},[157,2406,2407],{"class":171},"resolve",[157,2409,2410],{"class":163},"(__dirname",[157,2412,241],{"class":167},[157,2414,272],{"class":231},[157,2416,2417],{"class":235},".env",[157,2419,232],{"class":231},[157,2421,2422],{"class":163},") ",[157,2424,210],{"class":167},[157,2426,213],{"class":163},[157,2428,204],{"class":167},[157,2430,2431],{"class":159,"line":253},[157,2432,221],{"emptyLinePlaceholder":51},[157,2434,2435,2437,2439,2441,2443],{"class":159,"line":301},[157,2436,854],{"class":193},[157,2438,857],{"class":193},[157,2440,831],{"class":171},[157,2442,175],{"class":862},[157,2444,866],{"class":865},[157,2446,2447,2449,2451,2453,2455,2457],{"class":159,"line":343},[157,2448,871],{"class":200},[157,2450,269],{"class":167},[157,2452,272],{"class":231},[157,2454,878],{"class":235},[157,2456,232],{"class":231},[157,2458,883],{"class":865},[157,2460,2461,2464,2466,2468],{"class":159,"line":421},[157,2462,2463],{"class":200},"  fullyParallel",[157,2465,269],{"class":167},[157,2467,987],{"class":986},[157,2469,883],{"class":865},[157,2471,2472,2474,2476],{"class":159,"line":437},[157,2473,888],{"class":200},[157,2475,269],{"class":167},[157,2477,893],{"class":163},[157,2479,2480],{"class":159,"line":450},[157,2481,898],{"class":167},[157,2483,2484,2486,2488,2490,2492,2494],{"class":159,"line":464},[157,2485,903],{"class":200},[157,2487,269],{"class":167},[157,2489,272],{"class":231},[157,2491,141],{"class":235},[157,2493,232],{"class":231},[157,2495,883],{"class":167},[157,2497,2498,2500,2502,2504,2506,2508,2510,2512,2514,2516,2518],{"class":159,"line":478},[157,2499,918],{"class":200},[157,2501,269],{"class":167},[157,2503,923],{"class":231},[157,2505,168],{"class":926},[157,2507,930],{"class":929},[157,2509,934],{"class":933},[157,2511,937],{"class":235},[157,2513,934],{"class":933},[157,2515,153],{"class":235},[157,2517,944],{"class":231},[157,2519,883],{"class":167},[157,2521,2522,2524,2526,2528,2530,2532,2534,2536,2539,2542,2545,2547,2550,2552,2554,2556,2558,2560,2562],{"class":159,"line":491},[157,2523,951],{"class":200},[157,2525,269],{"class":167},[157,2527,923],{"class":231},[157,2529,168],{"class":926},[157,2531,930],{"class":929},[157,2533,934],{"class":933},[157,2535,175],{"class":231},[157,2537,2538],{"class":235},"month-end",[157,2540,2541],{"class":929},"|",[157,2543,2544],{"class":235},"spanish",[157,2546,2541],{"class":929},[157,2548,2549],{"class":235},"new-checkout",[157,2551,213],{"class":231},[157,2553,934],{"class":933},[157,2555,937],{"class":235},[157,2557,934],{"class":933},[157,2559,153],{"class":235},[157,2561,944],{"class":231},[157,2563,883],{"class":167},[157,2565,2566,2569,2571,2573,2576,2578,2581,2583,2586,2588,2592],{"class":159,"line":505},[157,2567,2568],{"class":200},"      use",[157,2570,269],{"class":167},[157,2572,828],{"class":167},[157,2574,2575],{"class":200}," baseURL",[157,2577,269],{"class":167},[157,2579,2580],{"class":163}," process",[157,2582,168],{"class":167},[157,2584,2585],{"class":163},"env",[157,2587,168],{"class":167},[157,2589,2591],{"class":2590},"sQ79N","DEFAULT_BASE_URL",[157,2593,1336],{"class":167},[157,2595,2596],{"class":159,"line":521},[157,2597,994],{"class":167},[157,2599,2600],{"class":159,"line":534},[157,2601,898],{"class":167},[157,2603,2604,2606,2608,2610,2613,2615],{"class":159,"line":548},[157,2605,903],{"class":200},[157,2607,269],{"class":167},[157,2609,272],{"class":231},[157,2611,2612],{"class":235},"month-end-date",[157,2614,232],{"class":231},[157,2616,883],{"class":167},[157,2618,2619,2621,2623,2625,2627,2629,2631,2633,2635,2637,2639,2641,2643],{"class":159,"line":562},[157,2620,918],{"class":200},[157,2622,269],{"class":167},[157,2624,923],{"class":231},[157,2626,168],{"class":926},[157,2628,930],{"class":929},[157,2630,934],{"class":933},[157,2632,2538],{"class":235},[157,2634,934],{"class":933},[157,2636,937],{"class":235},[157,2638,934],{"class":933},[157,2640,153],{"class":235},[157,2642,944],{"class":231},[157,2644,883],{"class":167},[157,2646,2647,2649,2651,2653,2655,2657,2659,2661,2663,2665,2668],{"class":159,"line":576},[157,2648,2568],{"class":200},[157,2650,269],{"class":167},[157,2652,828],{"class":167},[157,2654,2575],{"class":200},[157,2656,269],{"class":167},[157,2658,2580],{"class":163},[157,2660,168],{"class":167},[157,2662,2585],{"class":163},[157,2664,168],{"class":167},[157,2666,2667],{"class":2590},"MONTH_END_BASE_URL",[157,2669,1336],{"class":167},[157,2671,2672],{"class":159,"line":581},[157,2673,994],{"class":167},[157,2675,2676],{"class":159,"line":586},[157,2677,898],{"class":167},[157,2679,2680,2682,2684,2686,2689,2691],{"class":159,"line":596},[157,2681,903],{"class":200},[157,2683,269],{"class":167},[157,2685,272],{"class":231},[157,2687,2688],{"class":235},"spanish-locale",[157,2690,232],{"class":231},[157,2692,883],{"class":167},[157,2694,2695,2697,2699,2701,2703,2705,2707,2709,2711,2713,2715,2717,2719],{"class":159,"line":601},[157,2696,918],{"class":200},[157,2698,269],{"class":167},[157,2700,923],{"class":231},[157,2702,168],{"class":926},[157,2704,930],{"class":929},[157,2706,934],{"class":933},[157,2708,2544],{"class":235},[157,2710,934],{"class":933},[157,2712,937],{"class":235},[157,2714,934],{"class":933},[157,2716,153],{"class":235},[157,2718,944],{"class":231},[157,2720,883],{"class":167},[157,2722,2723,2725,2727,2729,2731,2733,2735,2737,2739,2741,2744],{"class":159,"line":626},[157,2724,2568],{"class":200},[157,2726,269],{"class":167},[157,2728,828],{"class":167},[157,2730,2575],{"class":200},[157,2732,269],{"class":167},[157,2734,2580],{"class":163},[157,2736,168],{"class":167},[157,2738,2585],{"class":163},[157,2740,168],{"class":167},[157,2742,2743],{"class":2590},"SPANISH_BASE_URL",[157,2745,1336],{"class":167},[157,2747,2748],{"class":159,"line":631},[157,2749,994],{"class":167},[157,2751,2752],{"class":159,"line":647},[157,2753,898],{"class":167},[157,2755,2756,2758,2760,2762,2765,2767],{"class":159,"line":661},[157,2757,903],{"class":200},[157,2759,269],{"class":167},[157,2761,272],{"class":231},[157,2763,2764],{"class":235},"new-checkout-flag",[157,2766,232],{"class":231},[157,2768,883],{"class":167},[157,2770,2771,2773,2775,2777,2779,2781,2783,2785,2787,2789,2791,2793,2795],{"class":159,"line":666},[157,2772,918],{"class":200},[157,2774,269],{"class":167},[157,2776,923],{"class":231},[157,2778,168],{"class":926},[157,2780,930],{"class":929},[157,2782,934],{"class":933},[157,2784,2549],{"class":235},[157,2786,934],{"class":933},[157,2788,937],{"class":235},[157,2790,934],{"class":933},[157,2792,153],{"class":235},[157,2794,944],{"class":231},[157,2796,883],{"class":167},[157,2798,2799,2801,2803,2805,2807,2809,2811,2813,2815,2817,2820],{"class":159,"line":672},[157,2800,2568],{"class":200},[157,2802,269],{"class":167},[157,2804,828],{"class":167},[157,2806,2575],{"class":200},[157,2808,269],{"class":167},[157,2810,2580],{"class":163},[157,2812,168],{"class":167},[157,2814,2585],{"class":163},[157,2816,168],{"class":167},[157,2818,2819],{"class":2590},"NEW_CHECKOUT_BASE_URL",[157,2821,1336],{"class":167},[157,2823,2824],{"class":159,"line":677},[157,2825,994],{"class":167},[157,2827,2828,2830],{"class":159,"line":683},[157,2829,1066],{"class":163},[157,2831,883],{"class":865},[157,2833,2834,2836,2838],{"class":159,"line":692},[157,2835,210],{"class":865},[157,2837,213],{"class":862},[157,2839,204],{"class":167},[11,2841,2842,2843,2845,2846,2848,2849,2853],{},"Each environment flavor gets its own base URL in a ",[72,2844,2417],{}," file, which the config loads with ",[72,2847,2356],{}," as the ",[127,2850],{"href":2851,"text":2852},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-parameterize","Playwright docs"," suggest:",[148,2855,2857],{"className":2225,"code":2856,"filename":2417,"language":2227,"meta":44,"style":44},"DEFAULT_BASE_URL=https:\u002F\u002Ftest.example.com\nMONTH_END_BASE_URL=https:\u002F\u002Ftest-month-end.example.com\nSPANISH_BASE_URL=https:\u002F\u002Ftest-es.example.com\nNEW_CHECKOUT_BASE_URL=https:\u002F\u002Ftest-new-checkout.example.com\n",[72,2858,2859,2868,2877,2886],{"__ignoreMap":44},[157,2860,2861,2863,2865],{"class":159,"line":160},[157,2862,2591],{"class":163},[157,2864,2237],{"class":929},[157,2866,2867],{"class":235},"https:\u002F\u002Ftest.example.com\n",[157,2869,2870,2872,2874],{"class":159,"line":45},[157,2871,2667],{"class":163},[157,2873,2237],{"class":929},[157,2875,2876],{"class":235},"https:\u002F\u002Ftest-month-end.example.com\n",[157,2878,2879,2881,2883],{"class":159,"line":207},[157,2880,2743],{"class":163},[157,2882,2237],{"class":929},[157,2884,2885],{"class":235},"https:\u002F\u002Ftest-es.example.com\n",[157,2887,2888,2890,2892],{"class":159,"line":218},[157,2889,2819],{"class":163},[157,2891,2237],{"class":929},[157,2893,2894],{"class":235},"https:\u002F\u002Ftest-new-checkout.example.com\n",[11,2896,2897,2898,2901,2902,2905,2906,2908],{},"A test file such as ",[72,2899,2900],{},"billing.month-end.spec.ts"," then uses relative navigation like ",[72,2903,2904],{},"page.goto('\u002Fbilling')",", and ",[72,2907,2315],{}," decides which environment it hits. In CI you'd set the same variables from your environment or secrets instead of a file.",[11,2910,2911,2912,2914],{},"The demo repo runs this configuration in one command with no ",[72,2913,1415],{}," and no separate steps:",[148,2916,2918],{"className":363,"code":2917,"filename":365,"language":366,"meta":44,"style":44},"npx playwright test -c playwright.environments.config.ts\n◇ injected env (4) from .env.example\n\n\u001b[2mRunning \u001b[22m10\u001b[2m tests using \u001b[22m10\u001b[2m workers\u001b[22m\n\n◇ injected env (0) from .env.example\n◇ injected env (0) from .env.example\n◇ injected env (0) from .env.example\n  \u001b[32m✓\u001b[39m  \u001b[2m 1 \u001b[22m[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 1 runs against the default environment\u001b[2m (310ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 2 \u001b[22m[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 4 runs against the default environment\u001b[2m (313ms)\u001b[22m\n◇ injected env (0) from .env.example\n◇ injected env (0) from .env.example\n◇ injected env (0) from .env.example\n  \u001b[32m✓\u001b[39m  \u001b[2m 3 \u001b[22m[month-end-date] › tests-environments\\billing.month-end.spec.ts:7:9 › billing rollover (month-end environment) › rollover scenario 1\u001b[2m (319ms)\u001b[22m\n◇ injected env (0) from .env.example\n  \u001b[32m✓\u001b[39m  \u001b[2m 4 \u001b[22m[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 3 runs against the default environment\u001b[2m (314ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 5 \u001b[22m[month-end-date] › tests-environments\\billing.month-end.spec.ts:7:9 › billing rollover (month-end environment) › rollover scenario 2\u001b[2m (314ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 6 \u001b[22m[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 2 runs against the default environment\u001b[2m (311ms)\u001b[22m\n◇ injected env (0) from .env.example\n  \u001b[32m✓\u001b[39m  \u001b[2m 7 \u001b[22m[spanish-locale] › tests-environments\\account.spanish.spec.ts:7:9 › account page (Spanish environment) › account scenario 2\u001b[2m (316ms)\u001b[22m\n◇ injected env (0) from .env.example\n  \u001b[32m✓\u001b[39m  \u001b[2m 8 \u001b[22m[new-checkout-flag] › tests-environments\\checkout.new-checkout.spec.ts:7:9 › checkout (new-checkout flag environment) › checkout scenario 1\u001b[2m (306ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m 9 \u001b[22m[new-checkout-flag] › tests-environments\\checkout.new-checkout.spec.ts:7:9 › checkout (new-checkout flag environment) › checkout scenario 2\u001b[2m (305ms)\u001b[22m\n◇ injected env (0) from .env.example\n  \u001b[32m✓\u001b[39m  \u001b[2m10 \u001b[22m[spanish-locale] › tests-environments\\account.spanish.spec.ts:7:9 › account page (Spanish environment) › account scenario 1\u001b[2m (306ms)\u001b[22m\n\n\u001b[32m  10 passed\u001b[39m\u001b[2m (726ms)\u001b[22m\n",[72,2919,2920,2925,2930,2934,2947,2951,2956,2960,2964,2975,2986,2990,2994,2998,3009,3013,3025,3036,3047,3051,3062,3066,3078,3089,3093,3104,3108],{"__ignoreMap":44},[157,2921,2922],{"class":159,"line":160},[157,2923,2924],{"class":373},"npx playwright test -c playwright.environments.config.ts\n",[157,2926,2927],{"class":159,"line":45},[157,2928,2929],{"class":373},"◇ injected env (4) from .env.example\n",[157,2931,2932],{"class":159,"line":207},[157,2933,221],{"emptyLinePlaceholder":51},[157,2935,2936,2938,2941,2943,2945],{"class":159,"line":218},[157,2937,402],{"class":401},[157,2939,2940],{"class":373},"10",[157,2942,408],{"class":401},[157,2944,2940],{"class":373},[157,2946,414],{"class":401},[157,2948,2949],{"class":159,"line":224},[157,2950,221],{"emptyLinePlaceholder":51},[157,2952,2953],{"class":159,"line":253},[157,2954,2955],{"class":373},"◇ injected env (0) from .env.example\n",[157,2957,2958],{"class":159,"line":301},[157,2959,2955],{"class":373},[157,2961,2962],{"class":159,"line":343},[157,2963,2955],{"class":373},[157,2965,2966,2968,2970,2973],{"class":159,"line":421},[157,2967,425],{"class":424},[157,2969,428],{"class":401},[157,2971,2972],{"class":373},"[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 1 runs against the default environment",[157,2974,1832],{"class":401},[157,2976,2977,2979,2981,2984],{"class":159,"line":437},[157,2978,425],{"class":424},[157,2980,442],{"class":401},[157,2982,2983],{"class":373},"[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 4 runs against the default environment",[157,2985,1632],{"class":401},[157,2987,2988],{"class":159,"line":450},[157,2989,2955],{"class":373},[157,2991,2992],{"class":159,"line":464},[157,2993,2955],{"class":373},[157,2995,2996],{"class":159,"line":478},[157,2997,2955],{"class":373},[157,2999,3000,3002,3004,3007],{"class":159,"line":491},[157,3001,425],{"class":424},[157,3003,455],{"class":401},[157,3005,3006],{"class":373},"[month-end-date] › tests-environments\\billing.month-end.spec.ts:7:9 › billing rollover (month-end environment) › rollover scenario 1",[157,3008,434],{"class":401},[157,3010,3011],{"class":159,"line":505},[157,3012,2955],{"class":373},[157,3014,3015,3017,3019,3022],{"class":159,"line":521},[157,3016,425],{"class":424},[157,3018,469],{"class":401},[157,3020,3021],{"class":373},"[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 3 runs against the default environment",[157,3023,3024],{"class":401}," (314ms)\n",[157,3026,3027,3029,3031,3034],{"class":159,"line":534},[157,3028,425],{"class":424},[157,3030,496],{"class":401},[157,3032,3033],{"class":373},"[month-end-date] › tests-environments\\billing.month-end.spec.ts:7:9 › billing rollover (month-end environment) › rollover scenario 2",[157,3035,3024],{"class":401},[157,3037,3038,3040,3042,3045],{"class":159,"line":548},[157,3039,425],{"class":424},[157,3041,539],{"class":401},[157,3043,3044],{"class":373},"[parallel-safe] › tests-environments\\pricing.spec.ts:8:9 › pricing (default environment) › quote 2 runs against the default environment",[157,3046,502],{"class":401},[157,3048,3049],{"class":159,"line":562},[157,3050,2955],{"class":373},[157,3052,3053,3055,3057,3060],{"class":159,"line":576},[157,3054,425],{"class":424},[157,3056,526],{"class":401},[157,3058,3059],{"class":373},"[spanish-locale] › tests-environments\\account.spanish.spec.ts:7:9 › account page (Spanish environment) › account scenario 2",[157,3061,1609],{"class":401},[157,3063,3064],{"class":159,"line":581},[157,3065,2955],{"class":373},[157,3067,3068,3070,3072,3075],{"class":159,"line":586},[157,3069,425],{"class":424},[157,3071,512],{"class":401},[157,3073,3074],{"class":373},"[new-checkout-flag] › tests-environments\\checkout.new-checkout.spec.ts:7:9 › checkout (new-checkout flag environment) › checkout scenario 1",[157,3076,3077],{"class":401}," (306ms)\n",[157,3079,3080,3082,3084,3087],{"class":159,"line":596},[157,3081,425],{"class":424},[157,3083,484],{"class":401},[157,3085,3086],{"class":373},"[new-checkout-flag] › tests-environments\\checkout.new-checkout.spec.ts:7:9 › checkout (new-checkout flag environment) › checkout scenario 2",[157,3088,518],{"class":401},[157,3090,3091],{"class":159,"line":601},[157,3092,2955],{"class":373},[157,3094,3095,3097,3099,3102],{"class":159,"line":626},[157,3096,425],{"class":424},[157,3098,553],{"class":401},[157,3100,3101],{"class":373},"[spanish-locale] › tests-environments\\account.spanish.spec.ts:7:9 › account page (Spanish environment) › account scenario 1",[157,3103,3077],{"class":401},[157,3105,3106],{"class":159,"line":631},[157,3107,221],{"emptyLinePlaceholder":51},[157,3109,3110,3112],{"class":159,"line":647},[157,3111,1691],{"class":424},[157,3113,3114],{"class":401}," (726ms)\n",[11,3116,3117,3118,3120],{},"All four projects ran together, each handing its own ",[72,3119,2315],{}," to its tests. The demo's tests check that each project provides a set, valid base URL that differs from every other environment's, but they don't prove an environment is actually configured, since the example URLs don't point at real servers.",[11,3122,1093,3123,3126,3127,3129,3130,3133,3134,3137,3138,3141],{},[72,3124,3125],{},"injected env"," lines are printed by ",[72,3128,2356],{},", not Playwright. The demo loads ",[72,3131,3132],{},".env.example"," so it runs out of the box. The ",[72,3135,3136],{},"(4)"," is the main process loading the four base URLs, and the ",[72,3139,3140],{},"(0)"," lines appear as each worker starts and loads the config.",[1136,3143,3145],{"id":3144},"preconfigured-test-environments-the-cost","Preconfigured Test Environments: The Cost",[11,3147,3148],{},"Preconfigured test environments remove the need for serial tests since you can run those tests in parallel on isolated environments preconfigured with their preconditions where they won't pollute other tests running at the same time on other servers. This is the most performant of the solutions, but may not be possible for everyone.",[11,3150,3151],{},"For example, I want to use this approach, but I want these tests running on every pull request. We may have multiple pull requests that come in at a time and these preconfigured environments, which we provision from a base image with the PR applied, may need 5 or more flavors so that would mean creating 25 environments to support 5 pull requests. Since the system under test requires VMs instead of containers this is pretty expensive in provisioning time and quota.",[11,3153,3154],{},"Below are some things to evaluate:",[18,3156,3157,3163,3169,3175,3184],{},[21,3158,3159,3162],{},[92,3160,3161],{},"Hosting:"," Every flavor is another environment to pay for, and your hosting quota may not stretch to it.",[21,3164,3165,3168],{},[92,3166,3167],{},"Creating environments:"," It only works if you can stand up a preconfigured environment easily and repeatably. If a new environment takes days of manual setup, you won't keep several of them healthy.",[21,3170,3171,3174],{},[92,3172,3173],{},"Drift:"," Each flavor has to stay close to the baseline environment. A month-end environment that has quietly fallen behind the default one tests a different system.",[21,3176,3177,3180,3181,3183],{},[92,3178,3179],{},"Hardcoded URLs:"," A test that calls an absolute URL ignores ",[72,3182,2315],{},", and so does any test that reaches the server some other way, like a database connection.",[21,3185,3186,3189],{},[92,3187,3188],{},"Provisioning:"," Playwright only routes the tests. You still have to build and configure the environments yourself.",[11,3191,3192],{},"If you can't afford it, options A and B above are still solid. They're slower, because the serial tests wait for the parallel ones and run one at a time, but they need no extra environments.",[83,3194,3196],{"id":3195},"which-approach-to-use-for-serial-and-parallel-playwright-tests","Which Approach to Use for Serial and Parallel Playwright Tests",[3198,3199,3200,3220],"table",{},[3201,3202,3203],"thead",{},[3204,3205,3206,3209,3214,3217],"tr",{},[3207,3208],"th",{},[3207,3210,3211,3212],{},"Option A: Project ",[72,3213,1415],{},[3207,3215,3216],{},"Option B: Separate CI steps",[3207,3218,3219],{},"Option C: Preconfigured environments",[3221,3222,3223,3237,3250,3262,3278,3291,3305],"tbody",{},[3204,3224,3225,3229,3232,3234],{},[3226,3227,3228],"td",{},"Tests change global state on the fly",[3226,3230,3231],{},"Yes",[3226,3233,3231],{},[3226,3235,3236],{},"No, the environment is already set up",[3204,3238,3239,3242,3244,3247],{},[3226,3240,3241],{},"Runs state-changing tests after parallel ones",[3226,3243,3231],{},[3226,3245,3246],{},"Yes, by step order",[3226,3248,3249],{},"No ordering needed, everything runs together",[3204,3251,3252,3255,3258,3260],{},[3226,3253,3254],{},"Runs state-changing tests when a parallel test fails",[3226,3256,3257],{},"No",[3226,3259,3231],{},[3226,3261,3231],{},[3204,3263,3264,3271,3273,3276],{},[3226,3265,3266,3267,3270],{},"Single ",[72,3268,3269],{},"npx playwright test"," command",[3226,3272,3231],{},[3226,3274,3275],{},"No, one command per project",[3226,3277,3231],{},[3204,3279,3280,3283,3286,3288],{},[3226,3281,3282],{},"Extra infrastructure",[3226,3284,3285],{},"None",[3226,3287,3285],{},[3226,3289,3290],{},"One environment per flavor",[3204,3292,3293,3296,3299,3302],{},[3226,3294,3295],{},"Speed",[3226,3297,3298],{},"Slowest",[3226,3300,3301],{},"Slow",[3226,3303,3304],{},"Fastest",[3204,3306,3307,3310,3313,3316],{},[3226,3308,3309],{},"Best when",[3226,3311,3312],{},"The serial tests only make sense if the rest passed, like a smoke gate",[3226,3314,3315],{},"The two suites are independent and you can't add environments",[3226,3317,3318],{},"You can stand up preconfigured environments at a reasonable cost",[11,3320,3321,3322,3324],{},"If the serial tests build on the parallel ones, ",[72,3323,1415],{}," is the simpler option and skipping them on failure is the right behavior. For tests that only need the environment to themselves, use separate CI steps. If you can afford the environments, use those.",[83,3326,3328],{"id":3327},"reduce-how-many-tests-need-to-run-serially","Reduce How Many Tests Need to Run Serially",[11,3330,3331],{},"Splitting the suites fixes when the tests run. It doesn't make the state-changing tests any less fragile, and options A and B still need the environment to themselves. Fewer tests in the serial project means faster runs, so it's worth asking of each one whether it really needs to change global state or could be rewritten to leverage Playwright features that can emulate some of these things.",[11,3333,3334,3335,3338,3339,3343],{},"For example, if a test only depends on the browser's clock, Playwright has a ",[72,3336,3337],{},"page.clock"," API for controlling it (see the ",[127,3340],{"href":3341,"text":3342},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Fclock","Playwright Clock docs","). I haven't used it. It wouldn't help my case anyway, because the date I care about is on the server and triggers backend business processes, but it might solve some time travel test cases for others.",[1136,3345,3347],{"id":3346},"testinfooutputpath-when-multiple-tests-save-the-same-file","testInfo.outputPath: When Multiple Tests Save the Same File",[11,3349,3350,3351,3354],{},"If you are running into situations where parallel files touch the same output file you can leverage Playwright ",[72,3352,3353],{},"testInfo.outputPath"," command which returns a path scoped to the current test to prevent parallel collisions.",[148,3356,3358],{"className":150,"code":3357,"filename":815,"language":153,"meta":44,"style":44},"import { test } from '@playwright\u002Ftest';\n\ntest('exports rates PDF', async ({ page }, testInfo) => {\n  \u002F\u002F ...some sort of file download scenario\n  const file = testInfo.outputPath('insurance-rates.pdf'); \u002F\u002F Scoped, won't conflict with other tests downloading the same-named file with different contents\n  await download.saveAs(filePath);\n  \u002F\u002F ...verify the file contents\n});\n",[72,3359,3360,3380,3384,3419,3424,3459,3480,3485],{"__ignoreMap":44},[157,3361,3362,3364,3366,3368,3370,3372,3374,3376,3378],{"class":159,"line":160},[157,3363,825],{"class":193},[157,3365,828],{"class":167},[157,3367,2248],{"class":163},[157,3369,294],{"class":167},[157,3371,836],{"class":193},[157,3373,272],{"class":231},[157,3375,841],{"class":235},[157,3377,232],{"class":231},[157,3379,204],{"class":167},[157,3381,3382],{"class":159,"line":45},[157,3383,221],{"emptyLinePlaceholder":51},[157,3385,3386,3388,3390,3392,3395,3397,3399,3401,3404,3408,3410,3413,3415,3417],{"class":159,"line":207},[157,3387,164],{"class":171},[157,3389,175],{"class":163},[157,3391,232],{"class":231},[157,3393,3394],{"class":235},"exports rates PDF",[157,3396,232],{"class":231},[157,3398,241],{"class":167},[157,3400,244],{"class":178},[157,3402,3403],{"class":167}," ({",[157,3405,3407],{"class":3406},"s2xgV"," page",[157,3409,1190],{"class":167},[157,3411,3412],{"class":3406}," testInfo",[157,3414,213],{"class":167},[157,3416,185],{"class":178},[157,3418,188],{"class":167},[157,3420,3421],{"class":159,"line":218},[157,3422,3423],{"class":1056},"  \u002F\u002F ...some sort of file download scenario\n",[157,3425,3426,3429,3432,3435,3437,3439,3442,3444,3446,3449,3451,3453,3456],{"class":159,"line":224},[157,3427,3428],{"class":178},"  const",[157,3430,3431],{"class":2590}," file",[157,3433,3434],{"class":929}," =",[157,3436,3412],{"class":163},[157,3438,168],{"class":167},[157,3440,3441],{"class":171},"outputPath",[157,3443,175],{"class":200},[157,3445,232],{"class":231},[157,3447,3448],{"class":235},"insurance-rates.pdf",[157,3450,232],{"class":231},[157,3452,213],{"class":200},[157,3454,3455],{"class":167},";",[157,3457,3458],{"class":1056}," \u002F\u002F Scoped, won't conflict with other tests downloading the same-named file with different contents\n",[157,3460,3461,3463,3466,3468,3471,3473,3476,3478],{"class":159,"line":253},[157,3462,194],{"class":193},[157,3464,3465],{"class":163}," download",[157,3467,168],{"class":167},[157,3469,3470],{"class":171},"saveAs",[157,3472,175],{"class":200},[157,3474,3475],{"class":163},"filePath",[157,3477,213],{"class":200},[157,3479,204],{"class":167},[157,3481,3482],{"class":159,"line":301},[157,3483,3484],{"class":1056},"  \u002F\u002F ...verify the file contents\n",[157,3486,3487,3489,3491],{"class":159,"line":343},[157,3488,210],{"class":167},[157,3490,213],{"class":163},[157,3492,204],{"class":167},[1136,3494,3496],{"id":3495},"test-locks-when-only-a-few-tests-share-the-resource","Test Locks: When Only a Few Tests Share the Resource",[11,3498,3499,3500,3504,3505,3508],{},"Playwright 1.63 added ",[127,3501],{"href":3502,"text":3503},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-parallel#test-locks","test locks",". Give the tests that touch the same shared resource the same named ",[72,3506,3507],{},"lock",", and Playwright never runs them at the same time, across files, workers and projects, while every other test keeps running in parallel.",[148,3510,3513],{"className":150,"code":3511,"filename":3512,"language":153,"meta":44,"style":44},"test('feature flag on enables new checkout', { lock: 'server-settings' }, async () => {\n  \u002F\u002F ...\n});\n\ntest('server date set to month end triggers billing rollover', { lock: 'server-settings' }, async () => {\n  \u002F\u002F ...\n});\n","tests-locks\u002Fmutators.spec.ts",[72,3514,3515,3553,3558,3566,3570,3606,3610],{"__ignoreMap":44},[157,3516,3517,3519,3521,3523,3525,3527,3529,3531,3534,3536,3538,3541,3543,3545,3547,3549,3551],{"class":159,"line":160},[157,3518,164],{"class":171},[157,3520,175],{"class":163},[157,3522,232],{"class":231},[157,3524,1246],{"class":235},[157,3526,232],{"class":231},[157,3528,241],{"class":167},[157,3530,828],{"class":167},[157,3532,3533],{"class":200}," lock",[157,3535,269],{"class":167},[157,3537,272],{"class":231},[157,3539,3540],{"class":235},"server-settings",[157,3542,232],{"class":231},[157,3544,1190],{"class":167},[157,3546,244],{"class":178},[157,3548,182],{"class":167},[157,3550,185],{"class":178},[157,3552,188],{"class":167},[157,3554,3555],{"class":159,"line":45},[157,3556,3557],{"class":1056},"  \u002F\u002F ...\n",[157,3559,3560,3562,3564],{"class":159,"line":207},[157,3561,210],{"class":167},[157,3563,213],{"class":163},[157,3565,204],{"class":167},[157,3567,3568],{"class":159,"line":218},[157,3569,221],{"emptyLinePlaceholder":51},[157,3571,3572,3574,3576,3578,3580,3582,3584,3586,3588,3590,3592,3594,3596,3598,3600,3602,3604],{"class":159,"line":224},[157,3573,164],{"class":171},[157,3575,175],{"class":163},[157,3577,232],{"class":231},[157,3579,236],{"class":235},[157,3581,232],{"class":231},[157,3583,241],{"class":167},[157,3585,828],{"class":167},[157,3587,3533],{"class":200},[157,3589,269],{"class":167},[157,3591,272],{"class":231},[157,3593,3540],{"class":235},[157,3595,232],{"class":231},[157,3597,1190],{"class":167},[157,3599,244],{"class":178},[157,3601,182],{"class":167},[157,3603,185],{"class":178},[157,3605,188],{"class":167},[157,3607,3608],{"class":159,"line":253},[157,3609,3557],{"class":1056},[157,3611,3612,3614,3616],{"class":159,"line":301},[157,3613,210],{"class":167},[157,3615,213],{"class":163},[157,3617,204],{"class":167},[11,3619,3620,3621,3623],{},"This is a good fit when a few tests only conflict with each other, like several tests that edit the same account setting. Those can stay in the main parallel suite instead of moving to the ",[72,3622,145],{}," project. In the demo, the two tests above overwrote each other's state and failed in every one of 5 runs when I removed the lock, and passed in every one of 5 runs with it.",[11,3625,3626,3627,3629,3630,3633],{},"It doesn't solve my problem, though. A lock only keeps apart the tests that hold it. The ",[72,3628,141],{}," tests that read the settings don't hold it, so they keep running while a locked test has the state changed. That's what ",[72,3631,3632],{},"npm run test:locks:partial"," shows in the demo:",[148,3635,3637],{"className":363,"code":3636,"filename":365,"language":366,"meta":44,"style":44},"npm run test:locks:partial\n\n> playwright-serial-parallel-dependencies-demo@1.0.0 test:locks:partial\n> playwright test -c playwright.locks.config.ts mutators readers-unlocked\n\n\n\u001b[2mRunning \u001b[22m6\u001b[2m tests using \u001b[22m5\u001b[2m workers\u001b[22m\n\n  \u001b[31m✘\u001b[39m  \u001b[2m3 \u001b[22m\u001b[31mtests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 3 renders with default settings\u001b[39m\u001b[2m (320ms)\u001b[22m\n  \u001b[31m✘\u001b[39m  \u001b[2m1 \u001b[22m\u001b[31mtests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 2 renders with default settings\u001b[39m\u001b[2m (322ms)\u001b[22m\n  \u001b[31m✘\u001b[39m  \u001b[2m5 \u001b[22m\u001b[31mtests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 1 renders with default settings\u001b[39m\u001b[2m (315ms)\u001b[22m\n  \u001b[31m✘\u001b[39m  \u001b[2m4 \u001b[22m\u001b[31mtests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 4 renders with default settings\u001b[39m\u001b[2m (322ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m2 \u001b[22mtests-locks\\mutators.spec.ts:13:5 › feature flag on enables new checkout\u001b[2m (518ms)\u001b[22m\n  \u001b[32m✓\u001b[39m  \u001b[2m6 \u001b[22mtests-locks\\mutators.spec.ts:19:5 › server date set to month end triggers billing rollover\u001b[2m (503ms)\u001b[22m\n\n\n\u001b[31m  1) tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 1 renders with default settings \u001b[39m\n\n    Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) \u002F\u002F Object.is equality\u001b[22m\n\n    Expected: \u001b[32m\"o\u001b[7mff\u001b[27m\"\u001b[39m\n    Received: \u001b[31m\"o\u001b[7mn\u001b[27m\"\u001b[39m\n\n    ...\n\n\u001b[31m  4 failed\u001b[39m\n\u001b[31m    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 1 renders with default settings \u001b[39m\n\u001b[31m    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 2 renders with default settings \u001b[39m\n\u001b[31m    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 3 renders with default settings \u001b[39m\n\u001b[31m    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 4 renders with default settings \u001b[39m\n\u001b[32m  2 passed\u001b[39m\u001b[2m (1.3s)\u001b[22m\n",[72,3638,3639,3644,3648,3653,3658,3662,3666,3679,3683,3695,3707,3720,3732,3745,3758,3762,3766,3771,3775,3793,3797,3807,3817,3821,3825,3829,3834,3839,3844,3849,3854],{"__ignoreMap":44},[157,3640,3641],{"class":159,"line":160},[157,3642,3643],{"class":373},"npm run test:locks:partial\n",[157,3645,3646],{"class":159,"line":45},[157,3647,221],{"emptyLinePlaceholder":51},[157,3649,3650],{"class":159,"line":207},[157,3651,3652],{"class":373},"> playwright-serial-parallel-dependencies-demo@1.0.0 test:locks:partial\n",[157,3654,3655],{"class":159,"line":218},[157,3656,3657],{"class":373},"> playwright test -c playwright.locks.config.ts mutators readers-unlocked\n",[157,3659,3660],{"class":159,"line":224},[157,3661,221],{"emptyLinePlaceholder":51},[157,3663,3664],{"class":159,"line":253},[157,3665,221],{"emptyLinePlaceholder":51},[157,3667,3668,3670,3673,3675,3677],{"class":159,"line":301},[157,3669,402],{"class":401},[157,3671,3672],{"class":373},"6",[157,3674,408],{"class":401},[157,3676,411],{"class":373},[157,3678,414],{"class":401},[157,3680,3681],{"class":159,"line":343},[157,3682,221],{"emptyLinePlaceholder":51},[157,3684,3685,3687,3690,3693],{"class":159,"line":421},[157,3686,509],{"class":508},[157,3688,3689],{"class":401},"  3 ",[157,3691,3692],{"class":508},"tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 3 renders with default settings",[157,3694,1586],{"class":401},[157,3696,3697,3699,3702,3705],{"class":159,"line":437},[157,3698,509],{"class":508},[157,3700,3701],{"class":401},"  1 ",[157,3703,3704],{"class":508},"tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 2 renders with default settings",[157,3706,1821],{"class":401},[157,3708,3709,3711,3714,3717],{"class":159,"line":450},[157,3710,509],{"class":508},[157,3712,3713],{"class":401},"  5 ",[157,3715,3716],{"class":508},"tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 1 renders with default settings",[157,3718,3719],{"class":401}," (315ms)\n",[157,3721,3722,3724,3727,3730],{"class":159,"line":464},[157,3723,509],{"class":508},[157,3725,3726],{"class":401},"  4 ",[157,3728,3729],{"class":508},"tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 4 renders with default settings",[157,3731,1821],{"class":401},[157,3733,3734,3736,3739,3742],{"class":159,"line":478},[157,3735,425],{"class":424},[157,3737,3738],{"class":401},"  2 ",[157,3740,3741],{"class":373},"tests-locks\\mutators.spec.ts:13:5 › feature flag on enables new checkout",[157,3743,3744],{"class":401}," (518ms)\n",[157,3746,3747,3749,3752,3755],{"class":159,"line":491},[157,3748,425],{"class":424},[157,3750,3751],{"class":401},"  6 ",[157,3753,3754],{"class":373},"tests-locks\\mutators.spec.ts:19:5 › server date set to month end triggers billing rollover",[157,3756,3757],{"class":401}," (503ms)\n",[157,3759,3760],{"class":159,"line":505},[157,3761,221],{"emptyLinePlaceholder":51},[157,3763,3764],{"class":159,"line":521},[157,3765,221],{"emptyLinePlaceholder":51},[157,3767,3768],{"class":159,"line":534},[157,3769,3770],{"class":508},"  1) tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 1 renders with default settings \n",[157,3772,3773],{"class":159,"line":548},[157,3774,221],{"emptyLinePlaceholder":51},[157,3776,3777,3779,3781,3783,3785,3787,3789,3791],{"class":159,"line":562},[157,3778,604],{"class":373},[157,3780,607],{"class":401},[157,3782,610],{"class":508},[157,3784,613],{"class":401},[157,3786,328],{"class":373},[157,3788,175],{"class":401},[157,3790,620],{"class":424},[157,3792,623],{"class":401},[157,3794,3795],{"class":159,"line":576},[157,3796,221],{"emptyLinePlaceholder":51},[157,3798,3799,3801,3803,3805],{"class":159,"line":581},[157,3800,634],{"class":373},[157,3802,637],{"class":424},[157,3804,641],{"class":640},[157,3806,644],{"class":424},[157,3808,3809,3811,3813,3815],{"class":159,"line":586},[157,3810,650],{"class":373},[157,3812,637],{"class":508},[157,3814,656],{"class":655},[157,3816,644],{"class":508},[157,3818,3819],{"class":159,"line":596},[157,3820,221],{"emptyLinePlaceholder":51},[157,3822,3823],{"class":159,"line":601},[157,3824,669],{"class":373},[157,3826,3827],{"class":159,"line":626},[157,3828,221],{"emptyLinePlaceholder":51},[157,3830,3831],{"class":159,"line":631},[157,3832,3833],{"class":508},"  4 failed\n",[157,3835,3836],{"class":159,"line":647},[157,3837,3838],{"class":508},"    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 1 renders with default settings \n",[157,3840,3841],{"class":159,"line":661},[157,3842,3843],{"class":508},"    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 2 renders with default settings \n",[157,3845,3846],{"class":159,"line":666},[157,3847,3848],{"class":508},"    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 3 renders with default settings \n",[157,3850,3851],{"class":159,"line":672},[157,3852,3853],{"class":508},"    tests-locks\\readers-unlocked.spec.ts:9:9 › reports (no lock) › report 4 renders with default settings \n",[157,3855,3856,3859],{"class":159,"line":677},[157,3857,3858],{"class":424},"  2 passed",[157,3860,3861],{"class":401}," (1.3s)\n",[11,3863,3864],{},"All four unlocked tests failed in each of 5 runs on my machine. You could give those tests the same lock and everything passes, but then they run one at a time too: the demo took about 2.6 seconds that way, against 1.3 seconds with the readers unlocked. When a change affects every test, like the server date, putting a lock on everything is a serial run again so locking should be used sparingly.",[11,3866,3867,3868,3870],{},"Also note that a lock on any test in a spec file is held for the duration of the whole file. That means if one test in a spec file needs the lock, the other tests in that file count as holding it too, and a locked test in another file has to wait until the whole spec file finishes. Keeping the tests that need a lock in their own file, or turning on ",[72,3869,1103],{},", avoids that.",[83,3872,3874],{"id":3873},"takeaway-run-serial-and-parallel-playwright-tests-together-without-failures","Takeaway: Run Serial and Parallel Playwright Tests Together Without Failures",[11,3876,3877,3878,3880],{},"Tests that change global state don't have to force your whole suite to run serially. Mark them, match them with a filename or a tag, and give them their own project with ",[72,3879,740],{},". Then choose how the projects relate:",[18,3882,3883,3891,3897],{},[21,3884,3885,3890],{},[92,3886,3887,3888,241],{},"Option A, ",[72,3889,1415],{}," is the quick win when the serial tests should only run once everything else has passed.",[21,3892,3893,3896],{},[92,3894,3895],{},"Option B, separate CI steps,"," takes a little more wiring, but a red parallel run never hides your serial results.",[21,3898,3899,3902],{},[92,3900,3901],{},"Option C, preconfigured environments,"," is my long-term goal: if you can afford them, the tests never have to change global state at all.",[11,3904,3905,3906,168],{},"The demo repo has every variant if you want to try them. For more, see my ",[24,3907,3908],{"href":52},"Playwright articles",[3910,3911],"read-next",{":items":3912},"[\"\u002Fsoftware-testing\u002Ftest-automation\u002Fwhat-would-you-stop-doing-when-ui-tests-are-flaky\",\"\u002Fsoftware-testing\u002Ftest-automation\u002Frun-fewer-tests-catch-the-same-bugs\",\"\u002Fsoftware-testing\u002Ftest-automation\u002Fstareast-2026-playwright-ai-cost-efficient-testing\",\"\u002Fsoftware-testing\u002Ftest-automation\u002Fbest-websites-for-practicing-test-automation\"]",[3914,3915,3916],"style",{},"html pre.shiki code .sZ-rw,html code.shiki .sZ-rw{--shiki-light:#90A4AE;--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .sPJuK,html code.shiki .sPJuK{--shiki-light:#39ADB5;--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .sb1SK,html code.shiki .sb1SK{--shiki-light:#6182B8;--shiki-default:#622CBC;--shiki-dark:#DBB7FF}html pre.shiki code .stWsX,html code.shiki .stWsX{--shiki-light:#9C3EDA;--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .sZTni,html code.shiki .sZTni{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#A0111F;--shiki-default-font-style:inherit;--shiki-dark:#FF9492;--shiki-dark-font-style:inherit}html pre.shiki code .sq0XF,html code.shiki .sq0XF{--shiki-light:#E53935;--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .sZi47,html code.shiki .sZi47{--shiki-light:#39ADB5;--shiki-default:#032563;--shiki-dark:#ADDCFF}html pre.shiki code .srGNg,html code.shiki .srGNg{--shiki-light:#91B859;--shiki-default:#032563;--shiki-dark:#ADDCFF}html .light .shiki span{color:var(--shiki-light);background:var(--shiki-light-bg);font-style:var(--shiki-light-font-style);font-weight:var(--shiki-light-font-weight);text-decoration:var(--shiki-light-text-decoration)}html.light .shiki span{color:var(--shiki-light);background:var(--shiki-light-bg);font-style:var(--shiki-light-font-style);font-weight:var(--shiki-light-font-weight);text-decoration:var(--shiki-light-text-decoration)}html .default .shiki span{color:var(--shiki-default);background:var(--shiki-default-bg);font-style:var(--shiki-default-font-style);font-weight:var(--shiki-default-font-weight);text-decoration:var(--shiki-default-text-decoration)}html .shiki span{color:var(--shiki-default);background:var(--shiki-default-bg);font-style:var(--shiki-default-font-style);font-weight:var(--shiki-default-font-weight);text-decoration:var(--shiki-default-text-decoration)}html .dark .shiki span{color:var(--shiki-dark);background:var(--shiki-dark-bg);font-style:var(--shiki-dark-font-style);font-weight:var(--shiki-dark-font-weight);text-decoration:var(--shiki-dark-text-decoration)}html.dark .shiki span{color:var(--shiki-dark);background:var(--shiki-dark-bg);font-style:var(--shiki-dark-font-style);font-weight:var(--shiki-dark-font-weight);text-decoration:var(--shiki-dark-text-decoration)}html pre.shiki code .sOsy2,html code.shiki .sOsy2{--shiki-light:#90A4AE;--shiki-default:#0e1116;--shiki-dark:#f0f3f6}html pre.shiki code .sFDEC,html code.shiki .sFDEC{--shiki-light:#90A4AE80;--shiki-default:#0e111680;--shiki-dark:#f0f3f680}html pre.shiki code .ssIYW,html code.shiki .ssIYW{--shiki-light:#91B859;--shiki-default:#024c1a;--shiki-dark:#26cd4d}html pre.shiki code .sCdEk,html code.shiki .sCdEk{--shiki-light:#39ADB5;--shiki-default:#1b7c83;--shiki-dark:#39c5cf}html pre.shiki code .sDbPz,html code.shiki .sDbPz{--shiki-light:#E53935;--shiki-default:#a0111f;--shiki-dark:#ff9492}html pre.shiki code .smSMi,html code.shiki .smSMi{--shiki-light:#E5393580;--shiki-default:#a0111f80;--shiki-dark:#ff949280}html pre.shiki code .sBsy4,html code.shiki .sBsy4{--shiki-light:#FAFAFA;--shiki-light-bg:#91B859;--shiki-default:#ffffff;--shiki-default-bg:#024c1a;--shiki-dark:#0a0c10;--shiki-dark-bg:#26cd4d}html pre.shiki code .sYBWh,html code.shiki .sYBWh{--shiki-light:#FAFAFA;--shiki-light-bg:#E53935;--shiki-default:#ffffff;--shiki-default-bg:#a0111f;--shiki-dark:#0a0c10;--shiki-dark-bg:#ff9492}html pre.shiki code .spoJ1,html code.shiki .spoJ1{--shiki-light:#E2931D;--shiki-default:#3f2200;--shiki-dark:#f0b72f}html pre.shiki code .sLHsM,html code.shiki .sLHsM{--shiki-light:#90A4AE;--shiki-default:#702C00;--shiki-dark:#FFB757}html pre.shiki code .sCRTB,html code.shiki .sCRTB{--shiki-light:#39ADB5;--shiki-default:#702C00;--shiki-dark:#FFB757}html pre.shiki code .spMcu,html code.shiki .spMcu{--shiki-light:#91B859;--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .sE6rD,html code.shiki .sE6rD{--shiki-light:#39ADB5;--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .szoTG,html code.shiki .szoTG{--shiki-light:#90A4AE;--shiki-light-font-weight:inherit;--shiki-default:#024C1A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .sTqCK,html code.shiki .sTqCK{--shiki-light:#FF5370;--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .s6g51,html code.shiki .s6g51{--shiki-light:#F76D47;--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .s_gjE,html code.shiki .s_gjE{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#66707B;--shiki-default-font-style:inherit;--shiki-dark:#BDC4CC;--shiki-dark-font-style:inherit}html pre.shiki code .sA8fK,html code.shiki .sA8fK{--shiki-light:#E2931D;--shiki-default:#702C00;--shiki-dark:#FFB757}html pre.shiki code .saWzx,html code.shiki .saWzx{--shiki-light:#E53935;--shiki-default:#024C1A;--shiki-dark:#72F088}html pre.shiki code .sQ79N,html code.shiki .sQ79N{--shiki-light:#90A4AE;--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .s2xgV,html code.shiki .s2xgV{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#702C00;--shiki-default-font-style:inherit;--shiki-dark:#FFB757;--shiki-dark-font-style:inherit}",{"title":44,"searchDepth":45,"depth":45,"links":3918},[3919,3920,3921,3922,3925,3928,3929,3932,3933,3937],{"id":85,"depth":45,"text":86},{"id":134,"depth":45,"text":135},{"id":727,"depth":45,"text":728},{"id":793,"depth":45,"text":794,"children":3923},[3924],{"id":1138,"depth":207,"text":1139},{"id":1408,"depth":45,"text":1409,"children":3926},[3927],{"id":1697,"depth":207,"text":1698},{"id":1922,"depth":45,"text":1923},{"id":2291,"depth":45,"text":2292,"children":3930},[3931],{"id":3144,"depth":207,"text":3145},{"id":3195,"depth":45,"text":3196},{"id":3327,"depth":45,"text":3328,"children":3934},[3935,3936],{"id":3346,"depth":207,"text":3347},{"id":3495,"depth":207,"text":3496},{"id":3873,"depth":45,"text":3874},"\u002Fimages\u002Fposts\u002Frun-parallel-and-serial-tests-in-playwright\u002Frun-parallel-and-serial-tests-in-playwright-cover.webp","2026-09-20","Playwright tests that change global state break parallel test runs. Learn different ways to run serial and parallel tests together without failures.",{},"\u002Fsoftware-testing\u002Fframeworks\u002Fplaywright\u002Frun-parallel-and-serial-tests-in-playwright",{"title":59,"description":3940},"software-testing\u002Fframeworks\u002Fplaywright\u002Frun-parallel-and-serial-tests-in-playwright","7UYaA6wuodZYYwHt2TfjNCuO5SPqAQW_G_KY0lObN8s",1789952824684]