Skip to content

Instantly share code, notes, and snippets.

@edumserrano
Last active December 18, 2023 18:40
Show Gist options
  • Select an option

  • Save edumserrano/5e878002c7dfb40d3126e28dbb29dd42 to your computer and use it in GitHub Desktop.

Select an option

Save edumserrano/5e878002c7dfb40d3126e28dbb29dd42 to your computer and use it in GitHub Desktop.
Playwright clean up obsolete snapshots
# 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;
@edumserrano
Copy link
Copy Markdown
Author

This gist relies on the fact that for my setup the tests run via Docker and the snapshotPathTemplate on playwright.config.ts is set as:

snapshotPathTemplate: "{testDir}/_snapshots/{platform}/{projectName}/{testFilePath}/{arg}{ext}",

What this means is that when I run my tests normally using Docker all the snapshots are created in a {testDir}/_snapshots/Linux folder and when I run the above script, because I'm using a Windows OS, the snapshots get created at a {testDir}/_snapshots/wind32 .

The linux directory is then used as the actualPath on the script and the win32 dir is used as the expectedPath when doing the diff.

@edumserrano
Copy link
Copy Markdown
Author

Thank you @brodie124 for developing the above . Just sharing his work 👏

@edumserrano
Copy link
Copy Markdown
Author

I've decided to improve the initial version of this script. I created the edumserrano/playwright-adventures repo to share some of the experiences I've had whilst using Playwright and one of them is a solution to deleting stale snapshots.

Check out the stale-screenshots-cleanup demo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment