[{"data":1,"prerenderedAt":311},["ShallowReactive",2],{"navigation_docs":3,"-learn-baselines-and-approval":93,"-learn-baselines-and-approval-surround":306},[4,41,46,89],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":40},"Learn","i-lucide-book-open","\u002Flearn","1.learn",[10,15,20,25,30,35],{"title":11,"path":12,"stem":13,"icon":14},"What visual regression testing is","\u002Flearn\u002Fwhat-is-visual-regression-testing","1.learn\u002F1.what-is-visual-regression-testing","i-lucide-eye",{"title":16,"path":17,"stem":18,"icon":19},"How image diffing works","\u002Flearn\u002Fhow-image-diffing-works","1.learn\u002F2.how-image-diffing-works","i-lucide-diff",{"title":21,"path":22,"stem":23,"icon":24},"Why screenshots flake","\u002Flearn\u002Fwhy-screenshots-flake","1.learn\u002F3.why-screenshots-flake","i-lucide-shuffle",{"title":26,"path":27,"stem":28,"icon":29},"Baselines and approval","\u002Flearn\u002Fbaselines-and-approval","1.learn\u002F4.baselines-and-approval","i-lucide-check-check",{"title":31,"path":32,"stem":33,"icon":34},"Running in CI","\u002Flearn\u002Frunning-in-ci","1.learn\u002F5.running-in-ci","i-lucide-workflow",{"title":36,"path":37,"stem":38,"icon":39},"Choosing a tool","\u002Flearn\u002Fchoosing-a-tool","1.learn\u002F6.choosing-a-tool","i-lucide-list-checks",false,{"title":42,"path":43,"stem":44,"icon":45},"Compare the tools","\u002Fcompare","2.compare","i-lucide-scale",{"title":47,"path":48,"stem":49,"children":50,"icon":53},"Manifest","\u002Fmanifest","3.manifest\u002F1.index",[51,54,59,64,69,74,79,84],{"title":52,"path":48,"stem":49,"icon":53},"The run manifest","i-lucide-file-json",{"title":55,"path":56,"stem":57,"icon":58},"The format","\u002Fmanifest\u002Fformat","3.manifest\u002F2.format","i-lucide-file-code",{"title":60,"path":61,"stem":62,"icon":63},"Reading manifests","\u002Fmanifest\u002Freading","3.manifest\u002F3.reading","i-lucide-book-open-check",{"title":65,"path":66,"stem":67,"icon":68},"Merging the manifests of a run","\u002Fmanifest\u002Fmerging","3.manifest\u002F4.merging","i-lucide-merge",{"title":70,"path":71,"stem":72,"icon":73},"Writing manifests","\u002Fmanifest\u002Fwriting","3.manifest\u002F5.writing","i-lucide-pencil-line",{"title":75,"path":76,"stem":77,"icon":78},"Converting to and from other formats","\u002Fmanifest\u002Fconverting","3.manifest\u002F6.converting","i-lucide-arrow-left-right",{"title":80,"path":81,"stem":82,"icon":83},"Reference","\u002Fmanifest\u002Freference","3.manifest\u002F7.reference","i-lucide-list",{"title":85,"path":86,"stem":87,"icon":88},"Schema reference","\u002Fmanifest\u002Fschema","3.manifest\u002F8.schema","i-lucide-braces",{"title":90,"path":91,"stem":92,"icon":14},"Viewer","\u002Fviewer","4.viewer",{"id":94,"title":26,"body":95,"description":299,"extension":300,"links":301,"meta":302,"navigation":303,"path":27,"seo":304,"stem":28,"__hash__":305},"docs\u002F1.learn\u002F4.baselines-and-approval.md",{"type":96,"value":97,"toc":289},"minimark",[98,102,107,131,137,144,148,151,167,174,178,205,208,212,222,226,261,265],[99,100,101],"p",{},"A baseline is a decision frozen into a PNG: \"this is what it should look like\". The workflow around baselines decides whether visual tests stay useful or turn into a chore.",[103,104,106],"h2",{"id":105},"where-baselines-live","Where baselines live",[99,108,109,113,114,118,119,126,127,130],{},[110,111,112],"strong",{},"In the repository",", next to the tests. Baselines version with the code: a branch that changes the header carries the header's new baselines, a revert reverts them, and ",[115,116,117],"code",{},"git blame"," says who approved what. Every local tool (Playwright, Vitest, jest-image-snapshot, BackstopJS, Roborazzi, pixsame) works this way. The costs are repository size, which ",[120,121,125],"a",{"href":122,"rel":123},"https:\u002F\u002Fgit-lfs.com",[124],"nofollow","Git LFS"," fixes if it becomes a problem, and the need for every contributor to produce identical pixels, which a ",[120,128,129],{"href":22},"pinned renderer"," fixes.",[99,132,133,136],{},[110,134,135],{},"In a service",", keyed by branch and commit. Hosted review tools (Percy, Chromatic, Argos, Applitools, Happo) and self-hosted reg-suit store baselines on their side, compare a build against the baseline of the branch it was forked from, and keep the history. Nothing lands in the repository, developers do not need matching environments, and the review UI comes with it. The costs are a dependency on the service for every run, pricing per screenshot, and baselines that are not reviewable as part of the code diff.",[99,138,139,140,143],{},"Both work. Repository baselines suit teams who want the whole state in git; service baselines suit teams with many contributors on many machines. The ",[120,141,142],{"href":43},"comparison"," lists which tools do which.",[103,145,147],{"id":146},"who-approves","Who approves",[99,149,150],{},"The reviewer of the pull request, not the author. A visual change approved by the person who made it is a visual change nobody looked at. The ideal flow:",[152,153,154,158,161,164],"ol",{},[155,156,157],"li",{},"The PR runs the tests. Failed comparisons are reported where the review happens: a check on the PR, a comment with the images, or a link to the review app.",[155,159,160],{},"The reviewer looks at baseline, actual and diff. The slider between baseline and actual is the fastest way to see what moved.",[155,162,163],{},"Each change is either a bug (the PR gets fixed) or intended (the reviewer approves it, and the actual image becomes the baseline).",[155,165,166],{},"The approval is recorded: a commit that updates the baseline files, or a status in the service.",[99,168,169,170,173],{},"Tools that only fail the build and leave the images in a CI artifact push step 2 onto whoever digs the artifact out, which is nobody. The reporting step is where the ",[120,171,172],{"href":48},"manifest"," comes in: a file that lists which comparisons need a human and where their images are, so a PR bot or review page can be built once and used with any tool.",[103,175,177],{"id":176},"approving-from-a-pull-request","Approving from a pull request",[99,179,180,181,184,185,188,189,192,193,196,197,200,201,204],{},"With repository baselines, \"approve\" means copying the actual image over the baseline and committing it to the PR branch. This can be a local command (",[115,182,183],{},"npx playwright test --update-snapshots",", Roborazzi's ",[115,186,187],{},"recordRoborazzi*",", BackstopJS's ",[115,190,191],{},"approve","), a CI job triggered by a comment or label that commits the files back, or a GitHub App that does it from a button. The manifest's ",[115,194,195],{},"images.actual.path"," and ",[115,198,199],{},"images.baseline.path"," are exactly the two paths such a job needs; ",[115,202,203],{},"summary.needsHuman"," says whether there is anything to do.",[99,206,207],{},"With service baselines, approval is a click in the service's UI, and the service marks the build as passed.",[103,209,211],{"id":210},"partial-approval-and-the-approve-all-trap","Partial approval and the \"approve all\" trap",[99,213,214,215,196,218,221],{},"A PR that changes a shared component can fail fifty screenshots. If the only button is \"approve all\", the fifty approvals happen without fifty looks. Prefer tools and flows that allow approving per screenshot, group the failures by cause (the manifest's ",[115,216,217],{},"keyHash",[115,219,220],{},"selectEntries"," exist for this), and make the summary show the biggest diffs first.",[103,223,225],{"id":224},"baseline-hygiene","Baseline hygiene",[227,228,229,235,249,255],"ul",{},[155,230,231,234],{},[110,232,233],{},"Delete baselines of deleted tests."," Orphaned PNGs accumulate silently. Some tools warn about unused baselines; a script that lists baselines no test references is five lines.",[155,236,237,240,241,244,245,248],{},[110,238,239],{},"Name baselines after what they show, not when they were taken."," ",[115,242,243],{},"checkout-summary-mobile.png"," survives refactors; ",[115,246,247],{},"test-3-0.png"," does not.",[155,250,251,254],{},[110,252,253],{},"Keep one baseline per thing you actually test."," One per browser and OS if you really run several; otherwise pin the renderer and keep one.",[155,256,257,260],{},[110,258,259],{},"Review baseline changes like code changes."," A PR that touches 40 PNGs deserves a reviewer who opens them. Services show them side by side; in GitHub, the rich diff for images does.",[103,262,264],{"id":263},"further-reading","Further reading",[227,266,267,275,281],{},[155,268,269,274],{},[120,270,273],{"href":271,"rel":272},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Ftest-snapshots#updating-screenshots",[124],"Updating screenshots"," in Playwright.",[155,276,277,280],{},[120,278,125],{"href":122,"rel":279},[124]," for repositories where baselines grow large.",[155,282,283,288],{},[120,284,287],{"href":285,"rel":286},"https:\u002F\u002Fgithub.com\u002Freg-viz\u002Freg-suit",[124],"reg-suit",", a self-hosted example of the service model, with S3 or GCS as the baseline store.",{"title":290,"searchDepth":291,"depth":291,"links":292},"",2,[293,294,295,296,297,298],{"id":105,"depth":291,"text":106},{"id":146,"depth":291,"text":147},{"id":176,"depth":291,"text":177},{"id":210,"depth":291,"text":211},{"id":224,"depth":291,"text":225},{"id":263,"depth":291,"text":264},"Where baselines live, who approves a change, how to keep history, and why the review step is the part of the workflow most teams get wrong.","md",null,{},{"icon":29},{"title":26,"description":299},"ViPe6OpSICZWLBN102zECIuMkSaXtT9-2AZ8OjhBx18",[307,309],{"title":21,"path":22,"stem":23,"description":308,"icon":24,"children":-1},"Fonts, animations, GPU rasterisation, scrollbars, timing and the one fix that removes most of it, rendering in a pinned environment.",{"title":31,"path":32,"stem":33,"description":310,"icon":34,"children":-1},"Artifacts, sharding, merging results from several machines, reporting on the pull request, and keeping the bill down.",1791993875572]