├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── quota-increase-request.md └── workflows │ └── ADOIntegration.yml ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md └── samples ├── .NET ├── .gitignore ├── MSTest │ ├── .runsettings │ ├── PlaywrightTestsMSTest.csproj │ ├── README.md │ └── Tests.cs └── NUnit │ ├── .github │ └── workflows │ │ └── dotnet.yml │ ├── .runsettings │ ├── BrowserTestWithArtifact.cs │ ├── ContextTestWithArtifact.cs │ ├── PageTestWithArtifact.cs │ ├── PlaywrightServiceSetup.cs │ ├── PlaywrightTestWithArtifact.cs │ ├── PlaywrightTestsNUnit.csproj │ ├── README.md │ └── Tests.cs └── get-started ├── .github └── workflows │ └── get-started.yml ├── .gitignore ├── README.md ├── azure-pipelines.yml ├── package-lock.json ├── package.json ├── playwright.config.ts ├── playwright.service.config.ts └── tests └── example.spec.ts /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]:[Describe your issue here]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Step 1 '...' 16 | 2. Step 2 '....' 17 | 3. Step 3 '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Setup information (please complete the following information):** 27 | - Azure Subscription Id: 28 | - Workspace Name: 29 | - Service trace Id (if available): 30 | - Log snippet (if available): 31 | - Browser on which tests are executed (if applicable): 32 | - Time range (in PST) when the issue was experienced: 33 | - Playwright Version: 34 | - Client machine OS: 35 | - OS requested on Service: 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: vvs11 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/quota-increase-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Quota increase request 3 | about: Contact the team to increase the quota 4 | title: Quota increase request 5 | labels: '' 6 | assignees: vvs11 7 | 8 | --- 9 | 10 | ## Quota Increase Request 11 | 12 | **Quota to Increase:** 13 | 14 | 15 | [Specify which quota you would like to increase] 16 | 17 | 18 | * [ ] Maximum number of workers for the workspace 19 | * [ ] Maximum number of workspaces per subscription 20 | * [ ] Throttling limits 21 | * [ ] Other 22 | 23 | 24 | 25 | **Business Justification:** 26 | 27 | [Please provide a detailed business justification for the quota extension. Explain why this increase is necessary for your project or use case.] 28 | 29 | **Requested New Quota Limit:** 30 | 31 | [Specify the new quota limit you are requesting. Please be specific about the quantity or value (e.g., 60 workers per workspace, 5 workspaces per subscription etc.).] 32 | 33 | **Additional Information:** 34 | 35 | [Include any additional information that may be relevant to your request, such as project deadlines, the impact of the quota increase on your work, etc.] 36 | 37 | **Setup Information:** 38 | 39 | - **Subscription ID:** [Your Azure subscription ID] 40 | - **Workspace ID:** [ID of the workspace where you want to request quota increase] 41 | 42 | **Disclaimer:** 43 | 44 | Please note that while we will do our best to accommodate your quota extension request, we may not be able to fulfill every request due to resource constraints. We will review your request and provide a response as soon as possible. 45 | -------------------------------------------------------------------------------- /.github/workflows/ADOIntegration.yml: -------------------------------------------------------------------------------- 1 | name: Sync issue to Azure DevOps work item 2 | 3 | on: 4 | issues: 5 | types: 6 | [opened, edited, closed, reopened] 7 | 8 | issue_comment: 9 | types: [created, edited, deleted] 10 | 11 | jobs: 12 | alert: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: danhellem/github-actions-issue-to-work-item@master 16 | env: 17 | ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}" 18 | github_token: "${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}" 19 | ado_organization: "devdiv" 20 | ado_project: "OnlineServices" 21 | ado_area_path: "OnlineServices\\PlaywrightService" 22 | ado_iteration_path: "OnlineServices\\Dilithium\\CY24 Q2" 23 | ado_wit: "Issue" 24 | ado_new_state: "New" 25 | ado_active_state: "Active" 26 | ado_close_state: "Closed" 27 | ado_bypassrules: false 28 | log_level: 100 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Playwright Testing preview 2 | 3 | Microsoft Playwright Testing is a fully managed service that uses the cloud to enable you to run Playwright tests with much higher parallelization across different operating system-browser combinations simultaneously. Along with this, it enables you to publish test results and artifacts collected by Playwright to the service. This means faster test runs with broader scenario coverage, which helps speed up delivery of features without sacrificing quality. 4 | 5 | Ready to get started? Jump into our [quickstart guide](#get-started)! 6 | 7 | https://github.com/microsoft/playwright-testing-service/assets/12104064/29b969ec-7106-4407-a34a-4f04756d16f7 8 | 9 | ## Useful Links 10 | - [Quickstart: Run end-to-end tests at scale](https://aka.ms/mpt/quickstart) 11 | - [Quickstart: Set up continuous end-to-end testing across different browsers and operating systems](https://aka.ms/mpt/ci) 12 | - [Explore features and benefits](https://aka.ms/mpt/about) 13 | - [Documentation](https://aka.ms/mpt/docs) 14 | - [Pricing](https://aka.ms/mpt/pricing) 15 | - [Share feedback](https://aka.ms/mpt/feedback) 16 | 17 | ## Get Started 18 | Follow these steps to run your existing Playwright test suite with the service. 19 | 20 | ### Prerequisites 21 | 22 | - An Azure account with an active subscription. If you don't have an Azure subscription, [create a free account](https://aka.ms/mpt/create-azure-subscription) before you begin. 23 | - Your Azure account must be assigned the [Owner](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#owner), [Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#contributor), or one of the [classic administrator roles](https://learn.microsoft.com/en-us/azure/role-based-access-control/rbac-and-directory-admin-roles#classic-subscription-administrator-roles). 24 | - A Playwright project. If you don't have one, you can clone this repository and navigate to samples/get-started. 25 | - Azure CLI. If you don't have Azure CLI, see [Install Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) 26 | 27 | ### Create a Workspace 28 | 29 | 1. Sign in to the [Playwright portal](https://aka.ms/mpt/portal) with your Azure account. 30 | 31 | 1. Create the Workspace. 32 | 33 | ![Create new workspace](https://github.com/microsoft/playwright-testing-service/assets/12104064/d571e86b-9d43-48ac-a2b7-63afb9bb86a8) 34 | 35 | |Field |Description | 36 | |---------|---------| 37 | |**Workspace Name** | A unique name to identify your workspace.
The name can't contain special characters or whitespace. | 38 | |**Azure Subscription** | Select an Azure subscription where you want to create the workspace. | 39 | |**Region** | This is where test run data will be stored for your workspace. | 40 | 41 | > [!NOTE] 42 | > If you don't see this screen, select an existing workspace and go to the next section. 43 | 44 | 45 | ### Install Microsoft Playwright Testing package 46 | 47 | To use the service, install Microsoft Playwright Testing package. 48 | 49 | ```npm 50 | npm init @azure/microsoft-playwright-testing 51 | ``` 52 | This generates `playwright.service.config.ts` file which serves to: 53 | 54 | - Direct and authenticate Playwright to the Microsoft Playwright Testing service. 55 | - Adds a reporter to publish test results and artifacts. 56 | 57 | If you already have this file, the package asks you to override it. 58 | 59 | > [!NOTE] 60 | > Make sure your project uses @playwright/test version 1.47 or above. 61 | 62 | 63 | ### Obtain region endpoint 64 | 65 | 1. In the [Playwright portal](https://aka.ms/mpt/portal), copy the command under **Add region endpoint in your set up**. 66 | 67 | ![Set workspace endpoint](https://github.com/microsoft/playwright-testing-service/assets/12104064/d81ca629-2b23-4d34-8b70-67b6f7061a83) 68 | 69 | The endpoint URL corresponds to the workspace region. You might see a different endpoint URL in the Playwright portal, depending on the region you selected when creating the workspace. 70 | 71 | > [!NOTE] 72 | > The region end point could be updated. Make sure you copy the latest value from [Playwright portal](https://aka.ms/mpt/portal) . 73 | 74 | ### Set up environment 75 | 76 | Ensure that the `PLAYWRIGHT_SERVICE_URL` that you obtained in previous step is available in your environment. 77 | 78 | We recommend using `dotenv` module to manage your environment. With `dotenv` you'll be using the `.env` file to define your environment variables. 79 | 80 | > [!IMPORTANT] 81 | > Don't forget to add `.env` file to your `.gitignore` file in order to not leak your secrets. 82 | 83 | ```sh 84 | npm i --save-dev dotenv 85 | ``` 86 | 87 | `.env` file 88 | ``` 89 | PLAYWRIGHT_SERVICE_URL={MY-REGION-ENDPOINT} 90 | ``` 91 | Make sure to replace the `{MY-REGION-ENDPOINT}` text placeholder with the value you copied earlier. 92 | 93 | ### Set up Authentication 94 | 95 | To run your Playwright tests in your Microsoft Playwright Testing workspace, you need to authenticate the Playwright client where you are running the tests with the service. This could be your local dev machine or CI machine. 96 | 97 | The service offers two authentication methods: Microsoft Entra ID and Access Tokens. 98 | 99 | Microsoft Entra ID uses your Azure credentials, requiring a sign-in to your Azure account for secure access. Alternatively, you can generate an access token from your Playwright workspace and use it in your setup. 100 | 101 | You can follow any one of the authentication methods below: 102 | 103 | > We strongly recommend using Microsoft Entra ID for authentication to the service. 104 | 105 | #### Set up authtication using Microsoft Entra ID 106 | 107 | Microsoft Entra ID is the default and recommended authentication for the service. From your local dev machine, you can use [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) to sign-in 108 | 109 | ```CLI 110 | az login 111 | ``` 112 | 113 | **NOTE**: If you are a part of multiple Microsoft Entra tenants, make sure you sign-in to the tenant where your workspace belongs. You can get the tenant id from Azure portal, see [Find your Microsoft Entra Tenant](https://learn.microsoft.com/azure/azure-portal/get-subscription-tenant-id#find-your-microsoft-entra-tenant). Once you get the ID, sign-in using the command `az login --tenant ` 114 | 115 | #### Set up authentication using access tokens 116 | 117 | You can generate an access token from your Playwright Testing workspace and use it in your setup. However, we strongly recommend Microsoft Entra ID for authentication due to its enhanced security. Access tokens, while convenient, function like long-lived passwords and are more susceptible to being compromised. 118 | 119 | 1. Authentication using access tokens is disabled by default. To enable, see [Enable access-token based authentication](https://aka.ms/mpt/authentication) 120 | 121 | 2. [Set up authentication using access tokens](https://aka.ms/mpt/access-token) 122 | 123 | > We strongly recommend using Microsoft Entra ID for authentication to the service. If you are using access tokens, see [How to Manage Access Tokens](https://aka.ms/mpt/access-token) 124 | 125 | ### Run the tests 126 | 127 | Run Playwright tests against browsers managed by the service using the configuration you created above. 128 | 129 | ```sh 130 | npx playwright test --config=playwright.service.config.ts --workers=20 131 | ``` 132 | 133 | ## Next steps 134 | - Run tests in a [CI/CD pipeline.](https://aka.ms/mpt/configure-pipeline) 135 | 136 | - Learn how to [manage access](https://aka.ms/mpt/manage-access) to the created workspace. 137 | 138 | - Experiment with different number of workers to [determine the optimal configuration of your test suite](https://aka.ms/mpt/parallelism). 139 | 140 | 141 | ## Contributing 142 | 143 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 144 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 145 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 146 | 147 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 148 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 149 | provided by the bot. You will only need to do this once across all repos using our CLA. 150 | 151 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 152 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 153 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 154 | 155 | ## Trademarks 156 | 157 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 158 | trademarks or logos is subject to and must follow 159 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 160 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 161 | Any use of third-party trademarks or logos is subject to those third-party's policies. 162 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | 2 | # Support for Microsoft Playwright Testing 3 | 4 | We're here to help you with any questions, issues, or concerns you may have while using [Your Project Name]. You have two options for getting support: 5 | 6 | ## 1. GitHub Issue Tracker 7 | 8 | If you encounter a bug, have a feature request, or need general assistance, you can create an issue on our GitHub repository [here](https://aka.ms/mpt/feedback) 9 | 10 | Our team will review your issue and provide assistance as soon as possible. Please be patient and responsive to any follow-up questions or requests for clarification. 11 | 12 | ## 2. Azure Customer Support 13 | 14 | You can also raise a support ticket with Azure Customer Support. This option is suitable for critical issues or situations where you prefer not to disclose certain information. To create a support ticket with Azure, follow these steps: 15 | 16 | 17 | 1. Log in to [Playwright Testing Portal](https://aka.ms/mpt/portal). 18 | 19 | 2. Select '?' icon on the top bar. 20 | 21 | 3. Select "Help + Support on Azure portal". This will take you to Azure Portal where you can raise a new support request. 22 | 23 | 24 | Thank you for using Microsoft Playwright Testing, we appreciate your feedback and support! 25 | -------------------------------------------------------------------------------- /samples/.NET/.gitignore: -------------------------------------------------------------------------------- 1 | # The following command works for downloading when using Git for Windows: 2 | # curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 3 | # 4 | # Download this file using PowerShell v3 under Windows with the following comand: 5 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 6 | # 7 | # or wget: 8 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.sln.docstates 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Rr]elease/ 18 | x64/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | # build folder is nowadays used for build scripts and should not be ignored 22 | #build/ 23 | 24 | # NuGet Packages 25 | *.nupkg 26 | # The packages folder can be ignored because of Package Restore 27 | **/packages/* 28 | # except build/, which is used as an MSBuild target. 29 | !**/packages/build/ 30 | # Uncomment if necessary however generally it will be regenerated when needed 31 | #!**/packages/repositories.config 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | *_i.c 38 | *_p.c 39 | *.ilk 40 | *.meta 41 | *.obj 42 | *.pch 43 | *.pdb 44 | *.pgc 45 | *.pgd 46 | *.rsp 47 | *.sbr 48 | *.tlb 49 | *.tli 50 | *.tlh 51 | *.tmp 52 | *.tmp_proj 53 | *.log 54 | *.vspscc 55 | *.vssscc 56 | .builds 57 | *.pidb 58 | *.scc 59 | 60 | # Visual C++ cache files 61 | ipch/ 62 | *.aps 63 | *.ncb 64 | *.opensdf 65 | *.sdf 66 | *.cachefile 67 | 68 | # Visual Studio profiler 69 | *.psess 70 | *.vsp 71 | *.vspx 72 | 73 | # Guidance Automation Toolkit 74 | *.gpState 75 | 76 | # ReSharper is a .NET coding add-in 77 | _ReSharper*/ 78 | *.[Rr]e[Ss]harper 79 | 80 | # TeamCity is a build add-in 81 | _TeamCity* 82 | 83 | # DotCover is a Code Coverage Tool 84 | *.dotCover 85 | 86 | # NCrunch 87 | *.ncrunch* 88 | .*crunch*.local.xml 89 | 90 | # Installshield output folder 91 | [Ee]xpress/ 92 | 93 | # DocProject is a documentation generator add-in 94 | DocProject/buildhelp/ 95 | DocProject/Help/*.HxT 96 | DocProject/Help/*.HxC 97 | DocProject/Help/*.hhc 98 | DocProject/Help/*.hhk 99 | DocProject/Help/*.hhp 100 | DocProject/Help/Html2 101 | DocProject/Help/html 102 | 103 | # Click-Once directory 104 | publish/ 105 | 106 | # Publish Web Output 107 | *.Publish.xml 108 | 109 | # Windows Azure Build Output 110 | csx 111 | *.build.csdef 112 | 113 | # Windows Store app package directory 114 | AppPackages/ 115 | 116 | # Others 117 | *.Cache 118 | ClientBin/ 119 | [Ss]tyle[Cc]op.* 120 | ~$* 121 | *~ 122 | *.dbmdl 123 | *.[Pp]ublish.xml 124 | *.pfx 125 | *.publishsettings 126 | modulesbin/ 127 | tempbin/ 128 | 129 | # EPiServer Site file (VPP) 130 | AppData/ 131 | 132 | # RIA/Silverlight projects 133 | Generated_Code/ 134 | 135 | # Backup & report files from converting an old project file to a newer 136 | # Visual Studio version. Backup files are not needed, because we have git ;-) 137 | _UpgradeReport_Files/ 138 | Backup*/ 139 | UpgradeLog*.XML 140 | UpgradeLog*.htm 141 | 142 | # vim 143 | *.txt~ 144 | *.swp 145 | *.swo 146 | 147 | # Temp files when opening LibreOffice on ubuntu 148 | .~lock.* 149 | 150 | # svn 151 | .svn 152 | 153 | # CVS - Source Control 154 | **/CVS/ 155 | 156 | # Remainings from resolving conflicts in Source Control 157 | *.orig 158 | 159 | # SQL Server files 160 | **/App_Data/*.mdf 161 | **/App_Data/*.ldf 162 | **/App_Data/*.sdf 163 | 164 | 165 | #LightSwitch generated files 166 | GeneratedArtifacts/ 167 | _Pvt_Extensions/ 168 | ModelManifest.xml 169 | 170 | # ========================= 171 | # Windows detritus 172 | # ========================= 173 | 174 | # Windows image file caches 175 | Thumbs.db 176 | ehthumbs.db 177 | 178 | # Folder config file 179 | Desktop.ini 180 | 181 | # Recycle Bin used on file shares 182 | $RECYCLE.BIN/ 183 | 184 | # OS generated files # 185 | Icon? 186 | 187 | # Mac desktop service store files 188 | .DS_Store 189 | 190 | # SASS Compiler cache 191 | .sass-cache 192 | 193 | # Visual Studio 2014 CTP 194 | **/*.sln.ide 195 | 196 | # Visual Studio temp something 197 | .vs/ 198 | 199 | # dotnet stuff 200 | project.lock.json 201 | 202 | # VS 2015+ 203 | *.vc.vc.opendb 204 | *.vc.db 205 | 206 | # Rider 207 | .idea/ 208 | 209 | # Visual Studio Code 210 | .vscode/ 211 | 212 | # Output folder used by Webpack or other FE stuff 213 | **/node_modules/* 214 | # **/wwwroot/* 215 | 216 | # SpecFlow specific 217 | *.feature.cs 218 | *.feature.xlsx.* 219 | *.Specs_*.html 220 | 221 | # UWP Projects 222 | AppPackages/ 223 | -------------------------------------------------------------------------------- /samples/.NET/MSTest/.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 20 7 | ClassLevel 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | chromium 20 | 5000 21 | 22 | true 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /samples/.NET/MSTest/PlaywrightTestsMSTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/.NET/MSTest/README.md: -------------------------------------------------------------------------------- 1 | # Scale Playwright .NET tests with Microsoft Playwright Testing (Experimental) 2 | 3 | This sample demonstrates how to run Playwright .NET tests at scale using Microsoft Playwright Testing. It showcases the benefits of accelerating test suite completion by leveraging more parallel cloud browsers. The tests are executed using MSTest test runner. 4 | 5 | Note: Since service integration is done via [playwright MSTEST base class](https://playwright.dev/dotnet/docs/test-runners) which uses BrowserService so it only works out of the box when you use one of the following base class BrowserTest, Page, ContextTest 6 | 7 | If you have not yet created a workspace, please follow the [Get Started guide](../../../README.md#get-started) 8 | 9 | ## Sample setup 10 | 1. Clone this sample: 11 | ```powershell 12 | git clone https://github.com/microsoft/playwright-testing-service 13 | cd playwright-testing-service/samples/.NET/MSTest 14 | ``` 15 | 16 | 1. Install dependencies: 17 | ```powershell 18 | dotnet add package Microsoft.Playwright.MSTest 19 | ``` 20 | 1. Build the project so the playwright.ps1 is available inside the bin directory: 21 | ```powershell 22 | dotnet build 23 | ``` 24 | 25 | 26 | 1. Install required browsers by replacing netX with the actual output folder name, e.g. net6.0: 27 | 28 | ```powershell 29 | pwsh bin/Debug/netX/playwright.ps1 install 30 | ``` 31 | 32 | If pwsh is not available, you have to [install PowerShell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell). 33 | 34 | 1. Set up Authentication using Access Tokens 35 | Currently, only access tokens are supported for NUnit. See [Set up authentication using access tokens](../../../README.md#set-up-authentication-using-access-tokens) 36 | 37 | 1. Set access token generated above as environment variable for your project: 38 | ```powershell 39 | $env:PLAYWRIGHT_SERVICE_ACCESS_TOKEN= # Paste Access Token value from previous step 40 | ``` 41 | NOTE: If you are using Playwright version 1.39, the name of this variable should be `PLAYWRIGHT_SERVICE_ACCESS_KEY`. 42 | 43 | 1. In the [Playwright portal](https://aka.ms/mpt/portal), copy the command under **Add region endpoint in your set up** and set the following environment variable: 44 | ```powershell 45 | $env:PLAYWRIGHT_SERVICE_URL= # Paste region endpoint URL 46 | ``` 47 | 48 | 49 | ## Run tests 50 | 51 | Run Playwright tests against browsers managed by the service using the configuration you created above. You can run up to 50 parallel workers with the service 52 | ```powershell 53 | dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=10 54 | ``` 55 | Note that by default MSTest will run all classes in parallel, while running tests inside each class sequentially (ExecutionScope.ClassLevel). You can adjust this behavior by using the following CLI parameter or using a .runsettings file as shown above. Running tests in parallel at the method level (ExecutionScope.MethodLevel) is not supported. Please refer to [Playwright documentation](https://playwright.dev/dotnet/docs/test-runners#running-mstest-tests-in-parallel). 56 | 57 | ## Add more configuration 58 | 59 | You can use the following environment variables to specify configuration parameters for the service: 60 | 61 | 62 | 1. **PLAYWRIGHT_SERVICE_EXPOSE_NETWORK**: This variable allows you to control the network access for your service. You can set it to `` to enable the service to access the localhost and test locally hosted applications. 63 | 64 | Example: 65 | ```powershell 66 | $env:PLAYWRIGHT_SERVICE_EXPOSE_NETWORK = "" 67 | ``` 68 | 69 | 2. **PLAYWRIGHT_SERVICE_OS**: Use this variable to specify the operating system where browsers are hosted. You can choose between 'linux' or 'windows' based on your requirements. 70 | 71 | Example: 72 | ```powershell 73 | $env:PLAYWRIGHT_SERVICE_OS = "linux" 74 | ``` 75 | 76 | 3. **PLAYWRIGHT_SERVICE_RUN_ID**: This variable allows you to change the ID of the test run. The run ID is a unique identifier for a test run and is used to track test runs in the portal. 77 | 78 | Example : 79 | ```powershell 80 | $env:PLAYWRIGHT_SERVICE_RUN_ID = "my_custom_runId" 81 | ``` 82 | -------------------------------------------------------------------------------- /samples/.NET/MSTest/Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Threading.Tasks; 3 | using Microsoft.Playwright; 4 | using Microsoft.Playwright.MSTest; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace PlaywrightTests; 8 | 9 | [TestClass] 10 | public class Tests : PageTest 11 | 12 | { 13 | [TestMethod] 14 | 15 | public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage() 16 | { 17 | 18 | await Page.GotoAsync("https://playwright.dev"); 19 | 20 | // Expect a title "to contain" a substring. 21 | await Expect(Page).ToHaveTitleAsync(new Regex("Playwright")); 22 | 23 | // create a locator 24 | var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }); 25 | 26 | // Expect an attribute "to be strictly equal" to the value. 27 | await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro"); 28 | 29 | // Click the get started link. 30 | await getStarted.ClickAsync(); 31 | 32 | // Expects the URL to contain intro. 33 | await Expect(Page).ToHaveURLAsync(new Regex(".*intro")); 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /samples/.NET/NUnit/.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: Nunit Playwright Test Sample 5 | # on: [push] 6 | 7 | permissions: # Required when using AuthType as EntraId 8 | id-token: write 9 | contents: read 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | env: 14 | working-directory: './samples/.NET/NUnit' 15 | environment: Production 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v4 20 | with: 21 | dotnet-version: 8.0.x 22 | - name: Restore dependencies 23 | run: dotnet restore 24 | working-directory: ${{env.working-directory}} 25 | - name: Build 26 | run: dotnet build --no-restore 27 | working-directory: ${{env.working-directory}} 28 | - name: Azure CLI Login # Required when using AuthType as EntraId 29 | uses: azure/login@v2 30 | with: 31 | client-id: ${{ secrets.AZURE_CLIENT_ID }} # Create ManagedIdentity in azure and assign required roles 32 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 33 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 34 | - name: Test 35 | run: dotnet test --no-build --verbosity normal --settings .runsettings 36 | working-directory: ${{env.working-directory}} 37 | env: 38 | PLAYWRIGHT_SERVICE_URL: ${{ secrets.PLAYWRIGHT_SERVICE_URL }} 39 | #PLAYWRIGHT_SERVICE_ACCESS_TOKEN: ${{ secrets.PLAYWRIGHT_SERVICE_ACCESS_TOKEN }} for AccessToken Auth 40 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/.runsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 10 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | chromium 29 | 5000 30 | 31 | false 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/BrowserTestWithArtifact.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Playwright; 2 | using Microsoft.Playwright.NUnit; 3 | using NUnit.Framework; 4 | using NUnit.Framework.Interfaces; 5 | 6 | namespace PlaywrightTests 7 | { 8 | /* 9 | * BrowserTestWithArtifact is a base class for tests that require Playwright page and context. 10 | * It provides a setup and teardown methods for the test to run in a browser context. 11 | * It also provides a way to enable trace, screenshot, and video artifacts for the test. 12 | * Users need to inherit this class instead of BrowserTest to write tests. 13 | */ 14 | [TestFixture] 15 | public class BrowserTestWithArtifact : BrowserTest 16 | { 17 | 18 | // Declare the Context and Page 19 | public IPage Page { get; private set; } = null!; 20 | public IBrowserContext Context { get; private set; } = null!; 21 | public virtual BrowserNewContextOptions ContextOptions() 22 | { 23 | return new() 24 | { 25 | Locale = "en-US", 26 | ColorScheme = ColorScheme.Light, 27 | RecordVideoDir = ".videos" 28 | }; 29 | } 30 | 31 | [SetUp] 32 | public async Task Setup() 33 | { 34 | // Create Context 35 | Context = await Browser.NewContextAsync(ContextOptions()); 36 | 37 | // Enable Trace 38 | await Context.Tracing.StartAsync(new() 39 | { 40 | Title = $"{TestContext.CurrentContext.Test.Name}", 41 | Screenshots = true, 42 | Snapshots = true, 43 | Sources = true 44 | }); 45 | // Create a new page 46 | Page = await Context.NewPageAsync(); 47 | } 48 | 49 | [TearDown] 50 | public async Task TearDown() 51 | { 52 | /* On Windows, Its possble that path exceed 255 characters. 53 | * if it does, feel free to change the path to a shorter one. 54 | * possibly removing test name. 55 | */ 56 | // Stop trace and add it as an attachment 57 | var tracePath = Path.Combine( 58 | TestContext.CurrentContext.WorkDirectory, 59 | "playwright-traces", 60 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.zip" 61 | ); 62 | await Context.Tracing.StopAsync(new() 63 | { 64 | Path = tracePath 65 | }); 66 | TestContext.AddTestAttachment(tracePath, description: "Trace"); 67 | 68 | // Take a screenshot on error and add it as an attachment 69 | if (TestContext.CurrentContext.Result.Outcome == ResultState.Error) 70 | { 71 | var screenshotPath = Path.Combine( 72 | TestContext.CurrentContext.WorkDirectory, 73 | "playwright-screenshot", 74 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.png"); 75 | await Page.ScreenshotAsync(new() 76 | { 77 | Path = screenshotPath, 78 | }); 79 | TestContext.AddTestAttachment(screenshotPath, description: "Screenshot"); 80 | } 81 | 82 | // Enable video artifact and add it as an attachment, Context close is required to save the video 83 | await Context.CloseAsync(); 84 | var videoPath = Path.Combine( 85 | TestContext.CurrentContext.WorkDirectory, 86 | "playwright-videos", 87 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.webm"); 88 | if (Page.Video != null) 89 | { 90 | await Page.Video.SaveAsAsync(videoPath); 91 | TestContext.AddTestAttachment(videoPath, description: "Video"); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/ContextTestWithArtifact.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Playwright; 2 | using Microsoft.Playwright.NUnit; 3 | using NUnit.Framework; 4 | using NUnit.Framework.Interfaces; 5 | 6 | namespace PlaywrightTests 7 | { 8 | /* 9 | * ContextTestWithArtifact is a base class for tests that require Playwright context and page. 10 | * It provides a setup and teardown methods for the test to run in a browser context. 11 | * It also provides a way to enable trace, screenshot, and video artifacts for the test. 12 | * Users need to inherit this class instead of ContextTest to write tests. 13 | */ 14 | [TestFixture] 15 | public class ContextTestWithArtifact : ContextTest 16 | { 17 | 18 | // Declare Page 19 | public IPage Page { get; private set; } = null!; 20 | 21 | [SetUp] 22 | public async Task Setup() 23 | { 24 | TestContext.WriteLine("Browser: " + BrowserName); 25 | // Enable Trace 26 | await Context.Tracing.StartAsync(new() 27 | { 28 | Title = $"{TestContext.CurrentContext.Test.Name}", 29 | Screenshots = true, 30 | Snapshots = true, 31 | Sources = true 32 | }); 33 | Page = await Context.NewPageAsync(); 34 | } 35 | 36 | [TearDown] 37 | public async Task TearDown() 38 | { 39 | /* On Windows, Its possble that path exceed 255 characters. 40 | * if it does, feel free to change the path to a shorter one. 41 | * possibly removing test name. 42 | */ 43 | // Stop trace and add it as an attachment 44 | var tracePath = Path.Combine( 45 | TestContext.CurrentContext.WorkDirectory, 46 | "playwright-traces", 47 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.zip" 48 | ); 49 | await Context.Tracing.StopAsync(new() 50 | { 51 | Path = tracePath 52 | }); 53 | TestContext.AddTestAttachment(tracePath, description: "Trace"); 54 | // Take a screenshot on error and add it as an attachment 55 | if (TestContext.CurrentContext.Result.Outcome == ResultState.Error) 56 | { 57 | var screenshotPath = Path.Combine( 58 | TestContext.CurrentContext.WorkDirectory, 59 | "playwright-screenshot", 60 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.png"); 61 | await Page.ScreenshotAsync(new() 62 | { 63 | Path = screenshotPath, 64 | }); 65 | TestContext.AddTestAttachment(screenshotPath, description: "Screenshot"); 66 | } 67 | 68 | // Enable video artifact and add it as an attachment, Context close is required to save the video 69 | await Context.CloseAsync(); 70 | var videoPath = Path.Combine( 71 | TestContext.CurrentContext.WorkDirectory, 72 | "playwright-videos", 73 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.webm"); 74 | if (Page.Video != null) 75 | { 76 | await Page.Video.SaveAsAsync(videoPath); 77 | TestContext.AddTestAttachment(videoPath, description: "Video"); 78 | } 79 | } 80 | 81 | public override BrowserNewContextOptions ContextOptions() 82 | { 83 | // Video enable via context option overriding the default context option. 84 | var options = base.ContextOptions(); 85 | options.RecordVideoDir = ".videos"; // temp path to enable video recording 86 | return options; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/PageTestWithArtifact.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Playwright; 2 | using Microsoft.Playwright.NUnit; 3 | using NUnit.Framework; 4 | using NUnit.Framework.Interfaces; 5 | 6 | namespace PlaywrightTests 7 | { 8 | /* 9 | * PageTestWithArtifact is a base class for tests that require Playwright page and context. 10 | * It provides a setup and teardown methods for the test to run in a browser context. 11 | * It also provides a way to enable trace, screenshot, and video artifacts for the test. 12 | * Users need to inherit this class instead of PageTest to write tests. 13 | */ 14 | [TestFixture] 15 | public class PageTestWithArtifact : PageTest 16 | { 17 | 18 | [SetUp] 19 | public async Task Setup() 20 | { 21 | TestContext.WriteLine("Browser: " + BrowserName); 22 | // Enable Trace 23 | await Context.Tracing.StartAsync(new() 24 | { 25 | Title = $"{TestContext.CurrentContext.Test.Name}", 26 | Screenshots = true, 27 | Snapshots = true, 28 | Sources = true 29 | }); 30 | } 31 | 32 | [TearDown] 33 | public async Task TearDown() 34 | { 35 | /* On Windows, Its possble that path exceed 255 characters. 36 | * if it does, feel free to change the path to a shorter one. 37 | * possibly removing test name. 38 | */ 39 | // Stop trace and add it as an attachment 40 | var tracePath = Path.Combine( 41 | TestContext.CurrentContext.WorkDirectory, 42 | "playwright-traces", 43 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.zip" 44 | ); 45 | await Context.Tracing.StopAsync(new() 46 | { 47 | Path = tracePath 48 | }); 49 | TestContext.AddTestAttachment(tracePath, description: "Trace"); 50 | 51 | // Take a screenshot on error and add it as an attachment 52 | if (TestContext.CurrentContext.Result.Outcome == ResultState.Error) 53 | { 54 | var screenshotPath = Path.Combine( 55 | TestContext.CurrentContext.WorkDirectory, 56 | "playwright-screenshot", 57 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.png"); 58 | await Page.ScreenshotAsync(new() 59 | { 60 | Path = screenshotPath, 61 | }); 62 | TestContext.AddTestAttachment(screenshotPath, description: "Screenshot"); 63 | } 64 | 65 | // Enable video artifact and add it as an attachment, Context close is required to save the video 66 | await Context.CloseAsync(); 67 | var videoPath = Path.Combine( 68 | TestContext.CurrentContext.WorkDirectory, 69 | "playwright-videos", 70 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.webm"); 71 | if (Page.Video != null) 72 | { 73 | await Page.Video.SaveAsAsync(videoPath); 74 | TestContext.AddTestAttachment(videoPath, description: "Video"); 75 | } 76 | } 77 | 78 | public override BrowserNewContextOptions ContextOptions() 79 | { 80 | // Video enable via context option overriding the default context option. 81 | var options = base.ContextOptions(); 82 | options.RecordVideoDir = ".videos"; // temp path to enable video recording 83 | return options; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/PlaywrightServiceSetup.cs: -------------------------------------------------------------------------------- 1 | using Azure.Developer.MicrosoftPlaywrightTesting.NUnit; 2 | using NUnit.Framework; 3 | 4 | namespace PlaywrightTests; // Remember to change this as per your project namespace 5 | 6 | [SetUpFixture] 7 | public class PlaywrightServiceSetup : PlaywrightServiceNUnit { }; -------------------------------------------------------------------------------- /samples/.NET/NUnit/PlaywrightTestWithArtifact.cs: -------------------------------------------------------------------------------- 1 | using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger; 2 | using Microsoft.Playwright; 3 | using Microsoft.Playwright.NUnit; 4 | using Microsoft.Playwright.TestAdapter; 5 | using NUnit.Framework; 6 | using NUnit.Framework.Interfaces; 7 | using System.Text.Json.Serialization; 8 | using System.Text.Json; 9 | 10 | namespace PlaywrightTests 11 | { 12 | /* 13 | * PlaywrightTestWithArtifact is a base class for tests that require Playwright context and page. 14 | * It provides a setup and teardown methods for the test to run in a browser context. 15 | * It also provides a way to enable trace, screenshot, and video artifacts for the test. 16 | * Users need to inherit this class instead of PlaywrightTest to write tests. 17 | */ 18 | [TestFixture] 19 | public class PlaywrightTestWithArtifact : PlaywrightTest 20 | { 21 | // Declare Browser, Context and Page 22 | public IPage Page { get; private set; } = null!; 23 | public IBrowserContext Context { get; private set; } = null!; 24 | public IBrowser Browser { get; private set; } = null!; 25 | 26 | public virtual BrowserNewContextOptions ContextOptions() 27 | { 28 | return new() 29 | { 30 | Locale = "en-US", 31 | ColorScheme = ColorScheme.Light, 32 | RecordVideoDir = ".videos" 33 | }; 34 | } 35 | 36 | [SetUp] 37 | public async Task Setup() 38 | { 39 | if (TestContext.Parameters.Get(RunSettingKey.UseCloudHostedBrowsers) == "false") 40 | { 41 | Browser = await BrowserType.LaunchAsync(PlaywrightSettingsProvider.LaunchOptions); 42 | } else { 43 | /* Connect Remote Browser using BrowserType.ConnectAsync 44 | * fetches service connect options like wsEndpoint and options 45 | * add x-playwright-launch-options header to pass launch options likes channel, headless, etc. 46 | */ 47 | var playwrightService = new PlaywrightService(); 48 | var connectOptions = await playwrightService.GetConnectOptionsAsync(); 49 | var launchOptionString = System.Text.Json.JsonSerializer.Serialize(PlaywrightSettingsProvider.LaunchOptions, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); 50 | if (connectOptions.Options!.Headers != null) 51 | { 52 | connectOptions.Options.Headers = connectOptions.Options.Headers.Concat(new Dictionary { { "x-playwright-launch-options", launchOptionString } }); 53 | } 54 | else 55 | { 56 | connectOptions.Options.Headers = new Dictionary { { "x-playwright-launch-options", launchOptionString } }; 57 | } 58 | Browser = await BrowserType.ConnectAsync(connectOptions.WsEndpoint!, connectOptions.Options!); 59 | } 60 | 61 | // Create context and page 62 | Context = await Browser.NewContextAsync(ContextOptions()); 63 | 64 | // Enable Trace 65 | await Context.Tracing.StartAsync(new() 66 | { 67 | Title = $"{TestContext.CurrentContext.Test.Name}", 68 | Screenshots = true, 69 | Snapshots = true, 70 | Sources = true 71 | }); 72 | // Create a new page 73 | Page = await Context.NewPageAsync(); 74 | } 75 | 76 | [TearDown] 77 | public async Task TearDown() 78 | { 79 | /* On Windows, Its possble that path exceed 255 characters. 80 | * if it does, feel free to change the path to a shorter one. 81 | * possibly removing test name. 82 | */ 83 | // Stop trace and add it as an attachment 84 | var tracePath = Path.Combine( 85 | TestContext.CurrentContext.WorkDirectory, 86 | "playwright-traces", 87 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.zip" 88 | ); 89 | await Context.Tracing.StopAsync(new() 90 | { 91 | Path = tracePath 92 | }); 93 | TestContext.AddTestAttachment(tracePath, description: "Trace"); 94 | 95 | // Take a screenshot on error and add it as an attachment 96 | if (TestContext.CurrentContext.Result.Outcome == ResultState.Error) 97 | { 98 | var screenshotPath = Path.Combine( 99 | TestContext.CurrentContext.WorkDirectory, 100 | "playwright-screenshot", 101 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.png"); 102 | await Page.ScreenshotAsync(new() 103 | { 104 | Path = screenshotPath, 105 | }); 106 | TestContext.AddTestAttachment(screenshotPath, description: "Screenshot"); 107 | } 108 | 109 | // Enable video artifact and add it as an attachment, Context close is required to save the video 110 | await Context.CloseAsync(); 111 | var videoPath = Path.Combine( 112 | TestContext.CurrentContext.WorkDirectory, 113 | "playwright-videos", 114 | $"{TestContext.CurrentContext.Test.Name}.{Guid.NewGuid()}.webm"); 115 | if (Page.Video != null) 116 | { 117 | await Page.Video.SaveAsAsync(videoPath); 118 | TestContext.AddTestAttachment(videoPath, description: "Video"); 119 | } 120 | await Browser.CloseAsync(); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/PlaywrightTestsNUnit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/README.md: -------------------------------------------------------------------------------- 1 | # Run Playwright .NET tests with Microsoft Playwright Testing 2 | 3 | This sample demonstrates how to run Playwright .NET tests at scale using Microsoft Playwright Testing. It showcases the benefits of accelerating test suite completion by leveraging more parallel cloud browsers. The tests are executed using NUnit test runner. 4 | 5 | Note: Since service integration is done via [playwright NUnit base class](https://playwright.dev/dotnet/docs/test-runners) which uses BrowserService so it only works out of the box when you use one of the following base class BrowserTest, Page, ContextTest 6 | 7 | If you have not yet created a workspace, please follow the [Get Started guide](../../../README.md#get-started) 8 | 9 | ### Sample setup 10 | 1. Clone this sample: 11 | ```powershell 12 | git clone https://github.com/microsoft/playwright-testing-service 13 | cd playwright-testing-service/samples/.NET/NUnit 14 | ``` 15 | 16 | 1. Install dependencies: 17 | ```powershell 18 | dotnet add package Microsoft.Playwright.NUnit 19 | ``` 20 | 1. Install service package 21 | ```powershell 22 | dotnet add package Azure.Developer.MicrosoftPlaywrightTesting.NUnit --prerelease 23 | ``` 24 | 25 | 1. Build the project so the playwright.ps1 is available inside the bin directory: 26 | ```powershell 27 | dotnet build 28 | ``` 29 | 30 | 1. Install required browsers by replacing netX with the actual output folder name, e.g. net6.0: 31 | 32 | ```powershell 33 | pwsh bin/Debug/netX/playwright.ps1 install 34 | ``` 35 | 36 | If pwsh is not available, you have to [install PowerShell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell). 37 | 38 | 1. Set up Authentication 39 | 40 | To run your Playwright tests in your Microsoft Playwright Testing workspace, you need to authenticate the Playwright client where you are running the tests with the service. This could be your local dev machine or CI machine. To run tests from your local machine, you can use Azure CLI. Run this command to sign-in 41 | 42 | ```CLI 43 | az login 44 | ``` 45 | **NOTE**: If you are a part of multiple Microsoft Entra tenants, make sure you sign-in to the tenant where your workspace belongs. You can get the tenant id from Azure portal, see [Find your Microsoft Entra Tenant](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-microsoft-entra-tenant). Once you get the id, sign in using the command `az login --tenant ` 46 | 47 | 1. Set up environment: 48 | In the [Playwright portal](https://aka.ms/mpt/portal), copy the command under **Add region endpoint in your set up** and set the following environment variable: 49 | 50 | ```bash 51 | PLAYWRIGHT_SERVICE_URL= # Paste region endpoint URL 52 | ``` 53 | 54 | ### Run tests 55 | 56 | Run Playwright tests against browsers managed by the service using the configuration you created above. You can run up to 50 parallel workers with the service 57 | ```powershell 58 | dotnet test --logger "microsoft-playwright-testing" -- NUnit.NumberOfTestWorkers=20 59 | ``` 60 | Note that by default NUnit will run all test files in parallel, while running tests inside each file sequentially (ParallelScope.Self). You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter. Running test in parallel using ParallelScope.All or ParallelScope.Fixtures is not supported. Please refer to [Playwright documentation](https://playwright.dev/dotnet/docs/test-runners#running-nunit-tests-in-parallel). 61 | 62 | 63 | ## Next steps 64 | 1. Follow the [quickstart guide](https://learn.microsoft.com/en-us/azure/playwright-testing/quickstart-run-end-to-end-tests?tabs=playwrightcli&pivots=nunit-test-runner) 65 | 2. [Integrate CI/CD pipelines](https://learn.microsoft.com/en-us/azure/playwright-testing/quickstart-automate-end-to-end-testing?tabs=github&pivots=nunit-test-runner) 66 | 3. Learn about [package options](https://learn.microsoft.com/en-us/azure/playwright-testing/how-to-use-service-config-file?pivots=nunit-test-runner). 67 | 68 | ### Details of the files on this sample project 69 | - [PlaywrightServiceSetup.cs](./PlaywrightServiceSetup.cs): **Requiried** to be added to your project to setup the service, make sure to change the namespace to your project namespace 70 | - [.runsettings](./.runsettings): Optional but recommended to be added to your project to better control service configuration params like Os, RunId, ServiceAuthType, UseCloudHostedBrowsers, ExposeNetwork 71 | - [PageTestWithArtifact.cs](./PageTestWithArtifact.cs): Optional but recommended, If you use PageTest fixture in your test class, this inherit class enable and attach artifacts to the test results 72 | - [ContextTestWithArtifact.cs](./ContextTestWithArtifact.cs): Optional but recommended, If you use ContextTest fixture in your test class, this inherit class enable and attach artifacts to the test results 73 | - [BrowserTestWithArtifact.cs](./BrowserTestWithArtifact.cs): Optional but recommended, If you use BrowserTest fixture in your test class, this inherit class enable and attach artifacts to the test results 74 | - [PlaywrightTestWithArtifact.cs](./PlaywrightTestWithArtifact.cs): Optional but recommended, If you use PlaywrightTest fixture in your test class, this inherit class enable and attach artifacts to the test results 75 | 76 | ### How to extend new fixtures 77 | - public class Tests : PageTestWithArtifact // for PageTest fixture 78 | - public class Tests : ContextTestWithArtifact // for ContextTest fixture 79 | - public class Tests : BrowserTestWithArtifact // for BrowserTest fixture 80 | - public class Tests : PlaywrightTestWithArtifact // for PlaywrightTest fixture 81 | 82 | -------------------------------------------------------------------------------- /samples/.NET/NUnit/Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Threading.Tasks; 3 | using Microsoft.Playwright; 4 | using Microsoft.Playwright.NUnit; 5 | using NUnit.Framework; 6 | 7 | namespace PlaywrightTests; 8 | 9 | [Parallelizable(ParallelScope.Self)] 10 | [TestFixture] 11 | public class Tests : PageTestWithArtifact 12 | { 13 | [Test] 14 | public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage() 15 | { 16 | await Page.GotoAsync("https://playwright.dev"); 17 | 18 | // Expect a title "to contain" a substring. 19 | await Expect(Page).ToHaveTitleAsync(new Regex("Playwright")); 20 | 21 | // create a locator 22 | var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }); 23 | 24 | // Expect an attribute "to be strictly equal" to the value. 25 | await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro"); 26 | 27 | // Click the get started link. 28 | await getStarted.ClickAsync(); 29 | 30 | // Expects the URL to contain intro. 31 | await Expect(Page).ToHaveURLAsync(new Regex(".*intro")); 32 | 33 | } 34 | 35 | [Test] 36 | public async Task RandomlyCaptureScreenshot() 37 | { 38 | await Page.GotoAsync("https://playwright.dev"); 39 | 40 | if (TestContext.CurrentContext.Random.Next(1, 100) % 3 == 0) 41 | { 42 | throw new Exception("This will happen randomly"); 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /samples/get-started/.github/workflows/get-started.yml: -------------------------------------------------------------------------------- 1 | # TODO: Place this file in the .github/workflows folder of your repository 2 | # This sample assumes the working directory is ./samples/get-started 3 | name: Get Started sample 4 | on: 5 | push: 6 | branches: [ main, master ] 7 | pull_request: 8 | branches: [ main, master ] 9 | workflow_dispatch: 10 | permissions: 11 | id-token: write 12 | contents: read 13 | jobs: 14 | test: 15 | timeout-minutes: 60 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: 18 22 | # This step is to sign-in to Azure to run tests from GitHub Action workflow, see: https://learn.microsoft.com/azure/developer/github/connect-from-azure 23 | - name: OIDC Login to Azure Public Cloud with AzPowershell (enableAzPSSession true) 24 | uses: azure/login@v2 25 | with: 26 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 27 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 28 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 29 | enable-AzPSSession: true 30 | 31 | - name: Install dependencies 32 | working-directory: samples/get-started 33 | run: npm install 34 | 35 | - name: Install MPT package 36 | working-directory: samples/get-started 37 | run: npm install @azure/microsoft-playwright-testing 38 | 39 | - name: Run Playwright tests 40 | working-directory: samples/get-started 41 | env: 42 | PLAYWRIGHT_SERVICE_URL: ${{ secrets.PLAYWRIGHT_SERVICE_URL }} 43 | PLAYWRIGHT_SERVICE_RUN_ID: ${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }} 44 | run: npx playwright test -c playwright.service.config.ts --workers=30 45 | - uses: actions/upload-artifact@v3 46 | if: always() 47 | with: 48 | name: playwright-report 49 | path: samples/get-started/playwright-report/ 50 | retention-days: 10 51 | -------------------------------------------------------------------------------- /samples/get-started/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /playwright/.cache/ 5 | .env 6 | .npmrc -------------------------------------------------------------------------------- /samples/get-started/README.md: -------------------------------------------------------------------------------- 1 | # Get Started Sample 2 | 3 | This sample uses a basic test to show how the service can speed up test suite completion with more parallel cloud browsers and publish test results and artifacts for easier troubleshooting. 4 | 5 | 6 | If you have not yet created a workspace, please follow the [Get Started guide](../../README.md#get-started) 7 | 8 | 9 | ## Sample setup 10 | 1. Clone this sample: 11 | ```bash 12 | git clone https://github.com/microsoft/playwright-testing-service 13 | cd playwright-testing-service/samples/get-started 14 | ``` 15 | 16 | 1. Install dependencies: 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | 1. Set up Authentication 22 | 23 | To run your Playwright tests in your Microsoft Playwright Testing workspace, you need to authenticate the Playwright client where you are running the tests with the service. This could be your local dev machine or CI machine. To run tests from your local machine, you can use Azure CLI. Run this command to sign-in 24 | 25 | ```CLI 26 | az login 27 | ``` 28 | **NOTE**: If you are a part of multiple Microsoft Entra tenants, make sure you sign-in to the tenant where your workspace belongs. You can get the tenant id from Azure portal, see [Find your Microsoft Entra Tenant](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-microsoft-entra-tenant). Once you get the id, sign in using the command `az login --tenant ` 29 | 30 | 1. Create a `.env` file in the sample's directory and define the following environment variable: 31 | In the [Playwright portal](https://aka.ms/mpt/portal), copy the command under **Add region endpoint in your set up** and set the following environment variable: 32 | ```bash 33 | PLAYWRIGHT_SERVICE_URL= # Paste region endpoint URL 34 | ``` 35 | 36 | ## Run tests 37 | 38 | Run Playwright tests against browsers managed by the service using the configuration you created above. You can run up to 50 parallel workers with the service 39 | 40 | ```bash 41 | npx playwright test --config=playwright.service.config.ts --workers=20 42 | ``` 43 | 44 | ## Run tests in a GitHub workflow 45 | 1. Copy the file [get-started.yaml](.github/workflows/get-started.yml) to your repository's `.github/workflows` directory. 46 | 1. Then follow the instructions in the article [Configure Playwright Service in a CI/CD pipeline](https://aka.ms/mpt/configure-pipeline). 47 | -------------------------------------------------------------------------------- /samples/get-started/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - none 8 | 9 | pool: 10 | vmImage: ubuntu-latest 11 | 12 | variables: 13 | "system.debug": false 14 | 15 | steps: 16 | 17 | - task: NodeTool@0 18 | inputs: 19 | versionSource: 'spec' 20 | versionSpec: '>18.x' 21 | 22 | 23 | - task: PowerShell@2 24 | enabled: true 25 | displayName: "Install dependencies" 26 | inputs: 27 | targetType: 'inline' 28 | script: 'npm ci' 29 | workingDirectory: '$(System.DefaultWorkingDirectory)/samples/get-started' 30 | 31 | - task: PowerShell@2 32 | enabled: true 33 | displayName: "Run Playwright tests" 34 | env: 35 | PLAYWRIGHT_SERVICE_ACCESS_TOKEN: $(Secret) 36 | inputs: 37 | targetType: 'inline' 38 | script: 'npx playwright test -c playwright.service.config.ts --workers=20' 39 | workingDirectory: '$(System.DefaultWorkingDirectory)/samples/get-started' 40 | 41 | - task: PublishPipelineArtifact@1 42 | inputs: 43 | targetPath: '$(System.DefaultWorkingDirectory)/samples/get-started/playwright-report/' 44 | artifact: 'Playwright tests' 45 | publishLocation: 'pipeline' -------------------------------------------------------------------------------- /samples/get-started/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-started-sample", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "get-started-sample", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@azure/microsoft-playwright-testing": "^1.0.0-beta.3", 13 | "@playwright/test": "^1.47", 14 | "@types/node": "^18.19.68", 15 | "dotenv": "^16.4.5" 16 | } 17 | }, 18 | "node_modules/@azure/abort-controller": { 19 | "version": "2.1.2", 20 | "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", 21 | "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", 22 | "dev": true, 23 | "license": "MIT", 24 | "dependencies": { 25 | "tslib": "^2.6.2" 26 | }, 27 | "engines": { 28 | "node": ">=18.0.0" 29 | } 30 | }, 31 | "node_modules/@azure/core-auth": { 32 | "version": "1.8.0", 33 | "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.8.0.tgz", 34 | "integrity": "sha512-YvFMowkXzLbXNM11yZtVLhUCmuG0ex7JKOH366ipjmHBhL3vpDcPAeWF+jf0X+jVXwFqo3UhsWUq4kH0ZPdu/g==", 35 | "dev": true, 36 | "license": "MIT", 37 | "dependencies": { 38 | "@azure/abort-controller": "^2.0.0", 39 | "@azure/core-util": "^1.1.0", 40 | "tslib": "^2.6.2" 41 | }, 42 | "engines": { 43 | "node": ">=18.0.0" 44 | } 45 | }, 46 | "node_modules/@azure/core-client": { 47 | "version": "1.9.2", 48 | "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", 49 | "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", 50 | "dev": true, 51 | "license": "MIT", 52 | "dependencies": { 53 | "@azure/abort-controller": "^2.0.0", 54 | "@azure/core-auth": "^1.4.0", 55 | "@azure/core-rest-pipeline": "^1.9.1", 56 | "@azure/core-tracing": "^1.0.0", 57 | "@azure/core-util": "^1.6.1", 58 | "@azure/logger": "^1.0.0", 59 | "tslib": "^2.6.2" 60 | }, 61 | "engines": { 62 | "node": ">=18.0.0" 63 | } 64 | }, 65 | "node_modules/@azure/core-http-compat": { 66 | "version": "2.1.2", 67 | "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz", 68 | "integrity": "sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ==", 69 | "dev": true, 70 | "license": "MIT", 71 | "dependencies": { 72 | "@azure/abort-controller": "^2.0.0", 73 | "@azure/core-client": "^1.3.0", 74 | "@azure/core-rest-pipeline": "^1.3.0" 75 | }, 76 | "engines": { 77 | "node": ">=18.0.0" 78 | } 79 | }, 80 | "node_modules/@azure/core-lro": { 81 | "version": "2.7.2", 82 | "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", 83 | "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", 84 | "dev": true, 85 | "license": "MIT", 86 | "dependencies": { 87 | "@azure/abort-controller": "^2.0.0", 88 | "@azure/core-util": "^1.2.0", 89 | "@azure/logger": "^1.0.0", 90 | "tslib": "^2.6.2" 91 | }, 92 | "engines": { 93 | "node": ">=18.0.0" 94 | } 95 | }, 96 | "node_modules/@azure/core-paging": { 97 | "version": "1.6.2", 98 | "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", 99 | "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", 100 | "dev": true, 101 | "license": "MIT", 102 | "dependencies": { 103 | "tslib": "^2.6.2" 104 | }, 105 | "engines": { 106 | "node": ">=18.0.0" 107 | } 108 | }, 109 | "node_modules/@azure/core-rest-pipeline": { 110 | "version": "1.17.0", 111 | "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.17.0.tgz", 112 | "integrity": "sha512-62Vv8nC+uPId3j86XJ0WI+sBf0jlqTqPUFCBNrGtlaUeQUIXWV/D8GE5A1d+Qx8H7OQojn2WguC8kChD6v0shA==", 113 | "dev": true, 114 | "license": "MIT", 115 | "dependencies": { 116 | "@azure/abort-controller": "^2.0.0", 117 | "@azure/core-auth": "^1.8.0", 118 | "@azure/core-tracing": "^1.0.1", 119 | "@azure/core-util": "^1.9.0", 120 | "@azure/logger": "^1.0.0", 121 | "http-proxy-agent": "^7.0.0", 122 | "https-proxy-agent": "^7.0.0", 123 | "tslib": "^2.6.2" 124 | }, 125 | "engines": { 126 | "node": ">=18.0.0" 127 | } 128 | }, 129 | "node_modules/@azure/core-tracing": { 130 | "version": "1.1.2", 131 | "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", 132 | "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", 133 | "dev": true, 134 | "license": "MIT", 135 | "dependencies": { 136 | "tslib": "^2.6.2" 137 | }, 138 | "engines": { 139 | "node": ">=18.0.0" 140 | } 141 | }, 142 | "node_modules/@azure/core-util": { 143 | "version": "1.10.0", 144 | "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.10.0.tgz", 145 | "integrity": "sha512-dqLWQsh9Nro1YQU+405POVtXnwrIVqPyfUzc4zXCbThTg7+vNNaiMkwbX9AMXKyoFYFClxmB3s25ZFr3+jZkww==", 146 | "dev": true, 147 | "license": "MIT", 148 | "dependencies": { 149 | "@azure/abort-controller": "^2.0.0", 150 | "tslib": "^2.6.2" 151 | }, 152 | "engines": { 153 | "node": ">=18.0.0" 154 | } 155 | }, 156 | "node_modules/@azure/core-xml": { 157 | "version": "1.4.3", 158 | "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.4.3.tgz", 159 | "integrity": "sha512-D6G7FEmDiTctPKuWegX2WTrS1enKZwqYwdKTO6ZN6JMigcCehlT0/CYl+zWpI9vQ9frwwp7GQT3/owaEXgnOsA==", 160 | "dev": true, 161 | "license": "MIT", 162 | "dependencies": { 163 | "fast-xml-parser": "^4.3.2", 164 | "tslib": "^2.6.2" 165 | }, 166 | "engines": { 167 | "node": ">=18.0.0" 168 | } 169 | }, 170 | "node_modules/@azure/identity": { 171 | "version": "4.4.1", 172 | "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.4.1.tgz", 173 | "integrity": "sha512-DwnG4cKFEM7S3T+9u05NstXU/HN0dk45kPOinUyNKsn5VWwpXd9sbPKEg6kgJzGbm1lMuhx9o31PVbCtM5sfBA==", 174 | "dev": true, 175 | "license": "MIT", 176 | "dependencies": { 177 | "@azure/abort-controller": "^1.0.0", 178 | "@azure/core-auth": "^1.5.0", 179 | "@azure/core-client": "^1.9.2", 180 | "@azure/core-rest-pipeline": "^1.1.0", 181 | "@azure/core-tracing": "^1.0.0", 182 | "@azure/core-util": "^1.3.0", 183 | "@azure/logger": "^1.0.0", 184 | "@azure/msal-browser": "^3.14.0", 185 | "@azure/msal-node": "^2.9.2", 186 | "events": "^3.0.0", 187 | "jws": "^4.0.0", 188 | "open": "^8.0.0", 189 | "stoppable": "^1.1.0", 190 | "tslib": "^2.2.0" 191 | }, 192 | "engines": { 193 | "node": ">=18.0.0" 194 | } 195 | }, 196 | "node_modules/@azure/identity/node_modules/@azure/abort-controller": { 197 | "version": "1.1.0", 198 | "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", 199 | "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", 200 | "dev": true, 201 | "license": "MIT", 202 | "dependencies": { 203 | "tslib": "^2.2.0" 204 | }, 205 | "engines": { 206 | "node": ">=12.0.0" 207 | } 208 | }, 209 | "node_modules/@azure/logger": { 210 | "version": "1.1.4", 211 | "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", 212 | "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", 213 | "dev": true, 214 | "license": "MIT", 215 | "dependencies": { 216 | "tslib": "^2.6.2" 217 | }, 218 | "engines": { 219 | "node": ">=18.0.0" 220 | } 221 | }, 222 | "node_modules/@azure/microsoft-playwright-testing": { 223 | "version": "1.0.0-beta.3", 224 | "resolved": "https://registry.npmjs.org/@azure/microsoft-playwright-testing/-/microsoft-playwright-testing-1.0.0-beta.3.tgz", 225 | "integrity": "sha512-GsElI6E5M4cPvmKvdHEMnz0EAeC4jp0fQgPFzJvZVHST+hnMvZBc6z8a3c+WAvPD4MRgsxT6JuDlNu7SeoFlXg==", 226 | "dev": true, 227 | "license": "MIT", 228 | "dependencies": { 229 | "@azure/core-rest-pipeline": "^1.16.3", 230 | "@azure/identity": "^4.3.1", 231 | "@azure/logger": "^1.1.4", 232 | "@azure/storage-blob": "^12.15.0", 233 | "tslib": "^2.6.0" 234 | }, 235 | "engines": { 236 | "node": ">=18.0.0" 237 | } 238 | }, 239 | "node_modules/@azure/msal-browser": { 240 | "version": "3.23.0", 241 | "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.23.0.tgz", 242 | "integrity": "sha512-+QgdMvaeEpdtgRTD7AHHq9aw8uga7mXVHV1KshO1RQ2uI5B55xJ4aEpGlg/ga3H+0arEVcRfT4ZVmX7QLXiCVw==", 243 | "dev": true, 244 | "license": "MIT", 245 | "dependencies": { 246 | "@azure/msal-common": "14.14.2" 247 | }, 248 | "engines": { 249 | "node": ">=0.8.0" 250 | } 251 | }, 252 | "node_modules/@azure/msal-common": { 253 | "version": "14.14.2", 254 | "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.2.tgz", 255 | "integrity": "sha512-XV0P5kSNwDwCA/SjIxTe9mEAsKB0NqGNSuaVrkCCE2lAyBr/D6YtD80Vkdp4tjWnPFwjzkwldjr1xU/facOJog==", 256 | "dev": true, 257 | "license": "MIT", 258 | "engines": { 259 | "node": ">=0.8.0" 260 | } 261 | }, 262 | "node_modules/@azure/msal-node": { 263 | "version": "2.13.1", 264 | "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.13.1.tgz", 265 | "integrity": "sha512-sijfzPNorKt6+9g1/miHwhj6Iapff4mPQx1azmmZExgzUROqWTM1o3ACyxDja0g47VpowFy/sxTM/WsuCyXTiw==", 266 | "dev": true, 267 | "license": "MIT", 268 | "dependencies": { 269 | "@azure/msal-common": "14.14.2", 270 | "jsonwebtoken": "^9.0.0", 271 | "uuid": "^8.3.0" 272 | }, 273 | "engines": { 274 | "node": ">=16" 275 | } 276 | }, 277 | "node_modules/@azure/storage-blob": { 278 | "version": "12.24.0", 279 | "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.24.0.tgz", 280 | "integrity": "sha512-l8cmWM4C7RoNCBOImoFMxhTXe1Lr+8uQ/IgnhRNMpfoA9bAFWoLG4XrWm6O5rKXortreVQuD+fc1hbzWklOZbw==", 281 | "dev": true, 282 | "license": "MIT", 283 | "dependencies": { 284 | "@azure/abort-controller": "^1.0.0", 285 | "@azure/core-auth": "^1.4.0", 286 | "@azure/core-client": "^1.6.2", 287 | "@azure/core-http-compat": "^2.0.0", 288 | "@azure/core-lro": "^2.2.0", 289 | "@azure/core-paging": "^1.1.1", 290 | "@azure/core-rest-pipeline": "^1.10.1", 291 | "@azure/core-tracing": "^1.1.2", 292 | "@azure/core-util": "^1.6.1", 293 | "@azure/core-xml": "^1.3.2", 294 | "@azure/logger": "^1.0.0", 295 | "events": "^3.0.0", 296 | "tslib": "^2.2.0" 297 | }, 298 | "engines": { 299 | "node": ">=18.0.0" 300 | } 301 | }, 302 | "node_modules/@azure/storage-blob/node_modules/@azure/abort-controller": { 303 | "version": "1.1.0", 304 | "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", 305 | "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", 306 | "dev": true, 307 | "license": "MIT", 308 | "dependencies": { 309 | "tslib": "^2.2.0" 310 | }, 311 | "engines": { 312 | "node": ">=12.0.0" 313 | } 314 | }, 315 | "node_modules/@playwright/test": { 316 | "version": "1.47.1", 317 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz", 318 | "integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==", 319 | "dev": true, 320 | "license": "Apache-2.0", 321 | "dependencies": { 322 | "playwright": "1.47.1" 323 | }, 324 | "bin": { 325 | "playwright": "cli.js" 326 | }, 327 | "engines": { 328 | "node": ">=18" 329 | } 330 | }, 331 | "node_modules/@types/node": { 332 | "version": "18.19.68", 333 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", 334 | "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", 335 | "dev": true, 336 | "license": "MIT", 337 | "dependencies": { 338 | "undici-types": "~5.26.4" 339 | } 340 | }, 341 | "node_modules/agent-base": { 342 | "version": "7.1.1", 343 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", 344 | "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", 345 | "dev": true, 346 | "license": "MIT", 347 | "dependencies": { 348 | "debug": "^4.3.4" 349 | }, 350 | "engines": { 351 | "node": ">= 14" 352 | } 353 | }, 354 | "node_modules/buffer-equal-constant-time": { 355 | "version": "1.0.1", 356 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 357 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", 358 | "dev": true, 359 | "license": "BSD-3-Clause" 360 | }, 361 | "node_modules/debug": { 362 | "version": "4.3.7", 363 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", 364 | "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", 365 | "dev": true, 366 | "license": "MIT", 367 | "dependencies": { 368 | "ms": "^2.1.3" 369 | }, 370 | "engines": { 371 | "node": ">=6.0" 372 | }, 373 | "peerDependenciesMeta": { 374 | "supports-color": { 375 | "optional": true 376 | } 377 | } 378 | }, 379 | "node_modules/define-lazy-prop": { 380 | "version": "2.0.0", 381 | "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", 382 | "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", 383 | "dev": true, 384 | "license": "MIT", 385 | "engines": { 386 | "node": ">=8" 387 | } 388 | }, 389 | "node_modules/dotenv": { 390 | "version": "16.4.5", 391 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", 392 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", 393 | "dev": true, 394 | "license": "BSD-2-Clause", 395 | "engines": { 396 | "node": ">=12" 397 | }, 398 | "funding": { 399 | "url": "https://dotenvx.com" 400 | } 401 | }, 402 | "node_modules/ecdsa-sig-formatter": { 403 | "version": "1.0.11", 404 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 405 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 406 | "dev": true, 407 | "license": "Apache-2.0", 408 | "dependencies": { 409 | "safe-buffer": "^5.0.1" 410 | } 411 | }, 412 | "node_modules/events": { 413 | "version": "3.3.0", 414 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 415 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 416 | "dev": true, 417 | "license": "MIT", 418 | "engines": { 419 | "node": ">=0.8.x" 420 | } 421 | }, 422 | "node_modules/fast-xml-parser": { 423 | "version": "4.5.0", 424 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", 425 | "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", 426 | "dev": true, 427 | "funding": [ 428 | { 429 | "type": "github", 430 | "url": "https://github.com/sponsors/NaturalIntelligence" 431 | }, 432 | { 433 | "type": "paypal", 434 | "url": "https://paypal.me/naturalintelligence" 435 | } 436 | ], 437 | "license": "MIT", 438 | "dependencies": { 439 | "strnum": "^1.0.5" 440 | }, 441 | "bin": { 442 | "fxparser": "src/cli/cli.js" 443 | } 444 | }, 445 | "node_modules/fsevents": { 446 | "version": "2.3.2", 447 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 448 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 449 | "dev": true, 450 | "hasInstallScript": true, 451 | "license": "MIT", 452 | "optional": true, 453 | "os": [ 454 | "darwin" 455 | ], 456 | "engines": { 457 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 458 | } 459 | }, 460 | "node_modules/http-proxy-agent": { 461 | "version": "7.0.2", 462 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", 463 | "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", 464 | "dev": true, 465 | "license": "MIT", 466 | "dependencies": { 467 | "agent-base": "^7.1.0", 468 | "debug": "^4.3.4" 469 | }, 470 | "engines": { 471 | "node": ">= 14" 472 | } 473 | }, 474 | "node_modules/https-proxy-agent": { 475 | "version": "7.0.5", 476 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", 477 | "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", 478 | "dev": true, 479 | "license": "MIT", 480 | "dependencies": { 481 | "agent-base": "^7.0.2", 482 | "debug": "4" 483 | }, 484 | "engines": { 485 | "node": ">= 14" 486 | } 487 | }, 488 | "node_modules/is-docker": { 489 | "version": "2.2.1", 490 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", 491 | "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", 492 | "dev": true, 493 | "license": "MIT", 494 | "bin": { 495 | "is-docker": "cli.js" 496 | }, 497 | "engines": { 498 | "node": ">=8" 499 | }, 500 | "funding": { 501 | "url": "https://github.com/sponsors/sindresorhus" 502 | } 503 | }, 504 | "node_modules/is-wsl": { 505 | "version": "2.2.0", 506 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 507 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 508 | "dev": true, 509 | "license": "MIT", 510 | "dependencies": { 511 | "is-docker": "^2.0.0" 512 | }, 513 | "engines": { 514 | "node": ">=8" 515 | } 516 | }, 517 | "node_modules/jsonwebtoken": { 518 | "version": "9.0.2", 519 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", 520 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", 521 | "dev": true, 522 | "license": "MIT", 523 | "dependencies": { 524 | "jws": "^3.2.2", 525 | "lodash.includes": "^4.3.0", 526 | "lodash.isboolean": "^3.0.3", 527 | "lodash.isinteger": "^4.0.4", 528 | "lodash.isnumber": "^3.0.3", 529 | "lodash.isplainobject": "^4.0.6", 530 | "lodash.isstring": "^4.0.1", 531 | "lodash.once": "^4.0.0", 532 | "ms": "^2.1.1", 533 | "semver": "^7.5.4" 534 | }, 535 | "engines": { 536 | "node": ">=12", 537 | "npm": ">=6" 538 | } 539 | }, 540 | "node_modules/jsonwebtoken/node_modules/jwa": { 541 | "version": "1.4.1", 542 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 543 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 544 | "dev": true, 545 | "license": "MIT", 546 | "dependencies": { 547 | "buffer-equal-constant-time": "1.0.1", 548 | "ecdsa-sig-formatter": "1.0.11", 549 | "safe-buffer": "^5.0.1" 550 | } 551 | }, 552 | "node_modules/jsonwebtoken/node_modules/jws": { 553 | "version": "3.2.2", 554 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 555 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 556 | "dev": true, 557 | "license": "MIT", 558 | "dependencies": { 559 | "jwa": "^1.4.1", 560 | "safe-buffer": "^5.0.1" 561 | } 562 | }, 563 | "node_modules/jwa": { 564 | "version": "2.0.0", 565 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 566 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 567 | "dev": true, 568 | "license": "MIT", 569 | "dependencies": { 570 | "buffer-equal-constant-time": "1.0.1", 571 | "ecdsa-sig-formatter": "1.0.11", 572 | "safe-buffer": "^5.0.1" 573 | } 574 | }, 575 | "node_modules/jws": { 576 | "version": "4.0.0", 577 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 578 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 579 | "dev": true, 580 | "license": "MIT", 581 | "dependencies": { 582 | "jwa": "^2.0.0", 583 | "safe-buffer": "^5.0.1" 584 | } 585 | }, 586 | "node_modules/lodash.includes": { 587 | "version": "4.3.0", 588 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 589 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", 590 | "dev": true, 591 | "license": "MIT" 592 | }, 593 | "node_modules/lodash.isboolean": { 594 | "version": "3.0.3", 595 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 596 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", 597 | "dev": true, 598 | "license": "MIT" 599 | }, 600 | "node_modules/lodash.isinteger": { 601 | "version": "4.0.4", 602 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 603 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", 604 | "dev": true, 605 | "license": "MIT" 606 | }, 607 | "node_modules/lodash.isnumber": { 608 | "version": "3.0.3", 609 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 610 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", 611 | "dev": true, 612 | "license": "MIT" 613 | }, 614 | "node_modules/lodash.isplainobject": { 615 | "version": "4.0.6", 616 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 617 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", 618 | "dev": true, 619 | "license": "MIT" 620 | }, 621 | "node_modules/lodash.isstring": { 622 | "version": "4.0.1", 623 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 624 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", 625 | "dev": true, 626 | "license": "MIT" 627 | }, 628 | "node_modules/lodash.once": { 629 | "version": "4.1.1", 630 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 631 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", 632 | "dev": true, 633 | "license": "MIT" 634 | }, 635 | "node_modules/ms": { 636 | "version": "2.1.3", 637 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 638 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 639 | "dev": true, 640 | "license": "MIT" 641 | }, 642 | "node_modules/open": { 643 | "version": "8.4.2", 644 | "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", 645 | "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", 646 | "dev": true, 647 | "license": "MIT", 648 | "dependencies": { 649 | "define-lazy-prop": "^2.0.0", 650 | "is-docker": "^2.1.1", 651 | "is-wsl": "^2.2.0" 652 | }, 653 | "engines": { 654 | "node": ">=12" 655 | }, 656 | "funding": { 657 | "url": "https://github.com/sponsors/sindresorhus" 658 | } 659 | }, 660 | "node_modules/playwright": { 661 | "version": "1.47.1", 662 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz", 663 | "integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==", 664 | "dev": true, 665 | "license": "Apache-2.0", 666 | "dependencies": { 667 | "playwright-core": "1.47.1" 668 | }, 669 | "bin": { 670 | "playwright": "cli.js" 671 | }, 672 | "engines": { 673 | "node": ">=18" 674 | }, 675 | "optionalDependencies": { 676 | "fsevents": "2.3.2" 677 | } 678 | }, 679 | "node_modules/playwright-core": { 680 | "version": "1.47.1", 681 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz", 682 | "integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==", 683 | "dev": true, 684 | "license": "Apache-2.0", 685 | "bin": { 686 | "playwright-core": "cli.js" 687 | }, 688 | "engines": { 689 | "node": ">=18" 690 | } 691 | }, 692 | "node_modules/safe-buffer": { 693 | "version": "5.2.1", 694 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 695 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 696 | "dev": true, 697 | "funding": [ 698 | { 699 | "type": "github", 700 | "url": "https://github.com/sponsors/feross" 701 | }, 702 | { 703 | "type": "patreon", 704 | "url": "https://www.patreon.com/feross" 705 | }, 706 | { 707 | "type": "consulting", 708 | "url": "https://feross.org/support" 709 | } 710 | ], 711 | "license": "MIT" 712 | }, 713 | "node_modules/semver": { 714 | "version": "7.6.3", 715 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 716 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 717 | "dev": true, 718 | "license": "ISC", 719 | "bin": { 720 | "semver": "bin/semver.js" 721 | }, 722 | "engines": { 723 | "node": ">=10" 724 | } 725 | }, 726 | "node_modules/stoppable": { 727 | "version": "1.1.0", 728 | "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", 729 | "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", 730 | "dev": true, 731 | "license": "MIT", 732 | "engines": { 733 | "node": ">=4", 734 | "npm": ">=6" 735 | } 736 | }, 737 | "node_modules/strnum": { 738 | "version": "1.0.5", 739 | "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", 740 | "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", 741 | "dev": true, 742 | "license": "MIT" 743 | }, 744 | "node_modules/tslib": { 745 | "version": "2.7.0", 746 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", 747 | "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", 748 | "dev": true, 749 | "license": "0BSD" 750 | }, 751 | "node_modules/undici-types": { 752 | "version": "5.26.5", 753 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 754 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 755 | "dev": true, 756 | "license": "MIT" 757 | }, 758 | "node_modules/uuid": { 759 | "version": "8.3.2", 760 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 761 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 762 | "dev": true, 763 | "license": "MIT", 764 | "bin": { 765 | "uuid": "dist/bin/uuid" 766 | } 767 | } 768 | } 769 | } 770 | -------------------------------------------------------------------------------- /samples/get-started/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-started-sample", 3 | "description": "Playwright Service get started sample", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "test": "npx playwright test -c playwright.service.config.ts --workers=20", 7 | "test-local": "npx playwright test" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@azure/microsoft-playwright-testing": "^1.0.0-beta.3", 14 | "@playwright/test": "^1.47", 15 | "@types/node": "^18.19.68", 16 | "dotenv": "^16.4.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/get-started/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './tests', 5 | fullyParallel: true, 6 | forbidOnly: !!process.env.CI, 7 | retries: process.env.CI ? 2 : 0, 8 | workers: process.env.CI ? 1 : undefined, 9 | reporter: 'html', 10 | use: { 11 | trace: 'on-first-retry', 12 | }, 13 | projects: [ 14 | { 15 | name: 'chromium', 16 | use: { ...devices['Desktop Chrome'] }, 17 | }, 18 | 19 | { 20 | name: 'firefox', 21 | use: { ...devices['Desktop Firefox'] }, 22 | }, 23 | 24 | { 25 | name: 'webkit', 26 | use: { ...devices['Desktop Safari'] }, 27 | }, 28 | ], 29 | }); 30 | -------------------------------------------------------------------------------- /samples/get-started/playwright.service.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@playwright/test'; 2 | import { getServiceConfig, ServiceOS } from '@azure/microsoft-playwright-testing'; 3 | import config from './playwright.config'; 4 | 5 | /* Learn more about service configuration at https://aka.ms/mpt/config */ 6 | export default defineConfig( 7 | config, 8 | getServiceConfig(config, { 9 | exposeNetwork: '', 10 | timeout: 30000, 11 | os: ServiceOS.LINUX, 12 | useCloudHostedBrowsers: true 13 | }), 14 | { 15 | /* 16 | Playwright Testing service reporter is added by default. 17 | This will override any reporter options specified in the base playwright config. 18 | If you are using more reporters, please update your configuration accordingly. 19 | */ 20 | reporter: [['list'], ['@azure/microsoft-playwright-testing/reporter']], 21 | } 22 | ); 23 | -------------------------------------------------------------------------------- /samples/get-started/tests/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | // This sample simulates a larger test suite 4 | const TEST_ITERATIONS = parseInt(process.env.TEST_ITERATIONS || "100"); 5 | for (var i = 0; i < TEST_ITERATIONS; i++) { 6 | 7 | test('has title ' + i, async ({ page }) => { 8 | await page.goto('https://playwright.dev/'); 9 | 10 | // Expect a title "to contain" a substring. 11 | await expect(page).toHaveTitle(/Playwright/); 12 | }); 13 | 14 | test('get started link ' + i, async ({ page }) => { 15 | await page.goto('https://playwright.dev/'); 16 | 17 | // Click the get started link. 18 | await page.getByRole('link', { name: 'Get started' }).click(); 19 | 20 | // Expects the URL to contain intro. 21 | await expect(page).toHaveURL(/.*intro/); 22 | }); 23 | 24 | } 25 | --------------------------------------------------------------------------------