# This script detects obsolete Playwright snapshots and automatically removes them. # Obsolete snapshots are detected by re-generating the snapshots in a different folder and then # comparing these two folders. # To do this, we are taking advantage of the fact that development/pipeline snapshots # are created using a Linux docker container, as we can re-generate the Playwright snapshots using # the locally-installed version to have them automatically created in a separate folder (i.e. `linux` vs `windows`). # # This script will be required so long as Playwright does not have built-in functionality to detect and clean up # obsolete snapshots. # See: https://github.com/microsoft/playwright/issues/16582 # # To run this script without removing an unused/obsolete snapshots and without removing the expected # snapshots directory, please use the -dryRun parameter. param( [string] $actualPath = "./tests/_snapshots/linux", [string] $expectedPath = "./tests/_snapshots/win32", [int] $maxAttempts = 5, [string] $dryRun = "no" ) function Convert2Bool($this) { return ($("False","0","","N","No",'$False',"Off") -notcontains [string]$this) } function EnforceWindowsOnlyExecution { if(-not $IsWindows) { Write-Host -ForegroundColor Red "❌ This script can only be executed on Windows."; exit 1; } } function GenerateExpectedSnapshots([int] $maxAttempts) { # Run Playwright to generate the expected snapshots $divider = "`n-------------------------------`n" Write-Host "$divider"; $runTestsCommand = "npx playwright test --update-snapshots"; $attemptsCount = 0; do { $attemptsCount++; Write-Host "Generating expected snapshots (attempt ${attemptsCount} / $maxAttempts)"; Invoke-Expression -Command $runTestsCommand; } until ($LASTEXITCODE -eq 0 -or $attemptsCount -ge $maxAttempts); Write-Host "$divider"; # Check if all attempts have failed if($LASTEXITCODE -ne 0) { Write-Host -ForegroundColor Red "❌ Failed to generate the expected snapshots after $attemptsCount attempts."; exit 1; } } function RemoveObsoleteSnapshots( [string] $actualPath, [string] $expectedPath, [bool] $dryRun) { # Determine which files are unused Write-Host "Searching for unused snapshots" $actualContents = Get-ChildItem -Recurse -File -Path "$actualPath"; $expectedContents = Get-ChildItem -Recurse -File -Path "$expectedPath"; $diffResults = @(Compare-Object -PassThru -Property Name -ReferenceObject $expectedContents -DifferenceObject $actualContents); $hasUnusedSnapshots = $False; foreach($item in $diffResults) { # Reference Only <= # Difference Only => if($item.SideIndicator -ne "=>") { continue; } # This file is an unused file (difference only) $fileFullPath = $item | Select-Object -ExpandProperty FullName; $fileRelativePath = $fileFullPath | Resolve-Path -Relative; $hasUnusedSnapshots = $True; if($dryRun) { Write-Host "* Unused snapshot detected: ${fileRelativePath}"; continue; } Write-Host "* Deleting unused snapshot: ${fileRelativePath}"; Remove-Item -Path $fileFullPath } if(-not $hasUnusedSnapshots) { Write-Host -ForegroundColor Yellow "No unused snapshots detected!"; } } function RemoveEmptyDirectories( [string] $actualPath, [bool] $dryRun) { if($dryRun) { Write-Host "Keeping empty directories (-dryRun specified)"; return; } # Check if there are any empty folders and remove them Write-Host "Removing empty directories" Get-ChildItem $actualPath -Recurse -Force -Directory | Sort-Object -Property FullName -Descending | Where-Object { $($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } | Remove-Item; } function RemoveExpectedSnapshotsDirectory([string] $expectedPath) { $expectedDirectoryExists = Test-Path -PathType Container -Path $expectedPath if(-not $expectedDirectoryExists) { Write-Host -ForegroundColor Yellow "Expected snapshots directory does not exist, skipping removal"; return; } # Remove the expected snapshots directory Write-Host "Removing expected snapshots directory"; Remove-Item -Recurse -Path $expectedPath; } function Main { $ErrorActionPreference = 'Stop' $dryRunAsBool = Convert2Bool($dryRun); EnforceWindowsOnlyExecution; RemoveExpectedSnapshotsDirectory -expectedPath $expectedPath; # Clean expected folder before starting GenerateExpectedSnapshots -maxAttempts $maxAttempts; RemoveObsoleteSnapshots -actualPath $actualPath -expectedPath $expectedPath -dryRun $dryRunAsBool; RemoveEmptyDirectories -actualPath $actualPath -dryRun $dryRunAsBool; # Remove empty directories in the $actualPath folder RemoveExpectedSnapshotsDirectory -expectedPath $expectedPath; # Also clean expected folder after completion Write-Host -ForegroundColor Green "✅ Finished cleaning playwright snapshots"; } Main;