Add repo Playwright smoke setup
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,6 +11,8 @@ artifacts/
|
|||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
node_modules/
|
node_modules/
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
|
|
||||||
# User secrets / configs
|
# User secrets / configs
|
||||||
appsettings.Development.json
|
appsettings.Development.json
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -70,7 +70,10 @@ Gameplay capabilities now include:
|
|||||||
|
|
||||||
- .NET SDK 10.0+
|
- .NET SDK 10.0+
|
||||||
- PowerShell 7+
|
- PowerShell 7+
|
||||||
|
- Node.js 22+
|
||||||
- Run `dotnet tool restore` once to enable the repo-local `dotnet-ef` command.
|
- Run `dotnet tool restore` once to enable the repo-local `dotnet-ef` command.
|
||||||
|
- Run `npm ci` once to install the repo-local Playwright toolchain.
|
||||||
|
- Run `npm exec playwright install chromium` once to install the browser used by local smoke tests.
|
||||||
|
|
||||||
## Local Development
|
## Local Development
|
||||||
|
|
||||||
@@ -84,6 +87,21 @@ Gameplay capabilities now include:
|
|||||||
```
|
```
|
||||||
3. Open `http://localhost:5000` (or the port shown in the console).
|
3. Open `http://localhost:5000` (or the port shown in the console).
|
||||||
|
|
||||||
|
Playwright helpers:
|
||||||
|
|
||||||
|
- Install/update browser dependencies:
|
||||||
|
```powershell
|
||||||
|
npm exec playwright install chromium
|
||||||
|
```
|
||||||
|
- Run the checked-in smoke test against an isolated temp SQLite database:
|
||||||
|
```powershell
|
||||||
|
pwsh ./scripts/run-playwright.ps1
|
||||||
|
```
|
||||||
|
- Run the Playwright suite directly when the app is already running:
|
||||||
|
```powershell
|
||||||
|
npm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
VS Code F5 debug profiles are available in `.vscode/launch.json`:
|
VS Code F5 debug profiles are available in `.vscode/launch.json`:
|
||||||
|
|
||||||
- `RpgRoller: Server`
|
- `RpgRoller: Server`
|
||||||
|
|||||||
76
package-lock.json
generated
Normal file
76
package-lock.json
generated
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"name": "rpgroller",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "rpgroller",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.59.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.59.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
|
||||||
|
"integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "1.59.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.59.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
|
||||||
|
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.59.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.59.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
|
||||||
|
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
package.json
Normal file
12
package.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "rpgroller",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"e2e": "playwright test",
|
||||||
|
"e2e:smoke": "playwright test tests/e2e/smoke.spec.js --reporter=line",
|
||||||
|
"e2e:install": "playwright install chromium"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.59.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
playwright.config.js
Normal file
13
playwright.config.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const { defineConfig } = require("@playwright/test");
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
testDir: "./tests/e2e",
|
||||||
|
timeout: 30_000,
|
||||||
|
fullyParallel: false,
|
||||||
|
reporter: "line",
|
||||||
|
use: {
|
||||||
|
baseURL: process.env.PLAYWRIGHT_BASE_URL || "http://127.0.0.1:5000",
|
||||||
|
headless: true,
|
||||||
|
trace: "retain-on-failure"
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
param(
|
param(
|
||||||
[switch]$SkipDotnetRestore,
|
[switch]$SkipDotnetRestore,
|
||||||
[switch]$SkipBuild
|
[switch]$SkipBuild,
|
||||||
|
[switch]$SkipPlaywright
|
||||||
)
|
)
|
||||||
|
|
||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
@@ -36,6 +37,14 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoke-Step -Name "Restore Node dependencies" -Action {
|
||||||
|
npm ci
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-Step -Name "Ensure Playwright browser" -Action {
|
||||||
|
npm exec playwright install chromium
|
||||||
|
}
|
||||||
|
|
||||||
Invoke-Step -Name "Run tests" -Action {
|
Invoke-Step -Name "Run tests" -Action {
|
||||||
if ($SkipBuild) {
|
if ($SkipBuild) {
|
||||||
dotnet test RpgRoller.Tests/RpgRoller.Tests.csproj --verbosity normal --collect:"XPlat Code Coverage" --settings RpgRoller.Tests/coverlet.runsettings
|
dotnet test RpgRoller.Tests/RpgRoller.Tests.csproj --verbosity normal --collect:"XPlat Code Coverage" --settings RpgRoller.Tests/coverlet.runsettings
|
||||||
@@ -49,6 +58,12 @@ try {
|
|||||||
pwsh ./scripts/check-coverage.ps1 -MinLineRate 0.90 -MinBranchRate 0.70
|
pwsh ./scripts/check-coverage.ps1 -MinLineRate 0.90 -MinBranchRate 0.70
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (-not $SkipPlaywright) {
|
||||||
|
Invoke-Step -Name "Run Playwright smoke test" -Action {
|
||||||
|
pwsh ./scripts/run-playwright.ps1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "CI checks passed."
|
Write-Host "CI checks passed."
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|||||||
61
scripts/run-playwright.ps1
Normal file
61
scripts/run-playwright.ps1
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
param(
|
||||||
|
[string]$BaseUrl = "http://127.0.0.1:5095",
|
||||||
|
[string]$Spec = "tests/e2e/smoke.spec.js"
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$repoRoot = Split-Path -Parent $scriptDir
|
||||||
|
$appUrl = [Uri]$BaseUrl
|
||||||
|
$healthUrl = "$BaseUrl/api/health"
|
||||||
|
$tempDbPath = Join-Path $env:TEMP ("rpgroller-playwright-{0}.db" -f [Guid]::NewGuid().ToString("N"))
|
||||||
|
$process = $null
|
||||||
|
|
||||||
|
Push-Location $repoRoot
|
||||||
|
try {
|
||||||
|
$env:ConnectionStrings__RpgRoller = "Data Source=$tempDbPath"
|
||||||
|
$env:PLAYWRIGHT_BASE_URL = $BaseUrl
|
||||||
|
|
||||||
|
$process = Start-Process dotnet -ArgumentList @(
|
||||||
|
"run",
|
||||||
|
"--project",
|
||||||
|
"RpgRoller/RpgRoller.csproj",
|
||||||
|
"--urls",
|
||||||
|
$BaseUrl
|
||||||
|
) -WorkingDirectory $repoRoot -PassThru
|
||||||
|
|
||||||
|
$response = $null
|
||||||
|
for ($i = 0; $i -lt 60; $i++) {
|
||||||
|
try {
|
||||||
|
$response = Invoke-WebRequest -Uri $healthUrl -UseBasicParsing -TimeoutSec 2
|
||||||
|
if ($response.StatusCode -eq 200) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
}
|
||||||
|
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $response -or $response.StatusCode -ne 200) {
|
||||||
|
throw "Application failed to start on $BaseUrl."
|
||||||
|
}
|
||||||
|
|
||||||
|
npm exec playwright test $Spec -- --reporter=line
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Playwright exited with code $LASTEXITCODE."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($process -and -not $process.HasExited) {
|
||||||
|
Stop-Process -Id $process.Id -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Remove-Item Env:\ConnectionStrings__RpgRoller -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item Env:\PLAYWRIGHT_BASE_URL -ErrorAction SilentlyContinue
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
11
tests/e2e/smoke.spec.js
Normal file
11
tests/e2e/smoke.spec.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const { test, expect } = require("@playwright/test");
|
||||||
|
|
||||||
|
test("home page loads auth entry points", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
await expect(page.locator("h1")).toContainText("RpgRoller");
|
||||||
|
await expect(page.getByRole("heading", { name: "Register" })).toBeVisible();
|
||||||
|
await expect(page.getByRole("heading", { name: "Login" })).toBeVisible();
|
||||||
|
await expect(page.getByLabel("Username").first()).toBeVisible();
|
||||||
|
await expect(page.getByLabel("Password").nth(1)).toBeVisible();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user