Skip to content

PER-8985 feat: Playwright drop-in baseline seeding — build attributes + exec orchestration#2335

Open
Shivanshu-07 wants to merge 3 commits into
masterfrom
feat/playwright-dropin-baseline
Open

PER-8985 feat: Playwright drop-in baseline seeding — build attributes + exec orchestration#2335
Shivanshu-07 wants to merge 3 commits into
masterfrom
feat/playwright-dropin-baseline

Conversation

@Shivanshu-07

Copy link
Copy Markdown
Contributor

What

CLI-side support for the @percy/playwright toHaveScreenshot drop-in's baseline flow:

  • @percy/clientcreateBuild accepts allow-listed drop-in build sources via PERCY_BUILD_SOURCE (playwright-dropin / playwright-dropin-baseline) plus dropin-baseline-candidate / dropin-baseline-setup attributes (env or explicit options). The API keys baseline auto-approval on these.
  • @percy/cli-exec — generic baseline-provider discovery: any installed SDK package can declare "@percy/cli": { "baselineProvider": "..." } (no framework code enters the CLI, same scaling model as command discovery). On an EMPTY project, percy exec uploads the committed baseline screenshots as an auto-approved build ⬆️ Bump @babel/register from 7.10.4 to 7.10.5 #1 before the user's command runs (the suite never executes for the baseline), then starts the head build. Established projects get a notice pointing at the SDK's explicit playwright:setup-baseline command — the API's no-build-persisted sentinel guarantees no stray builds. Seeding dispatches by project type: web → rendered web snapshots (root DOM + image resource, the percy upload shape); app → comparison ingest (tag + tile, no render).
  • @percy/core — the server-decided build source is kept on percy.build so SDKs can key on it through the healthcheck.

Depends on

Testing

Client + exec suites green incl. 11 new seeding specs; E2E verified against a local percy-api and on staging (empty web/app seed → head diff flow, established-project sentinel, explicit setup command).

🤖 Generated with Claude Code

Shivanshu-07 and others added 3 commits July 13, 2026 18:31
…orchestration

- @percy/client createBuild: allow-listed drop-in build sources via
  PERCY_BUILD_SOURCE, plus dropin-baseline-candidate / dropin-baseline-setup
  attributes (env or explicit options) the API keys baseline auto-approval on.
- @percy/cli-exec: generic baseline-provider discovery (any installed SDK
  package can declare "@percy/cli".baselineProvider). On an empty project,
  percy exec uploads the committed baseline screenshots as an auto-approved
  build #1 before the user's command runs; established projects get a notice
  pointing at the SDK's explicit setup command. Seeding helpers exported for
  SDK-contributed commands.
- @percy/core: expose the server-decided build source on percy.build so SDKs
  can key on it through the healthcheck.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified E2E against a local percy-api: a web project's snapshot create
requires a root resource, so raw-PNG comparison tiles are rejected. Seed
uploads now use client.sendSnapshot with the same generated root-DOM +
image-resource shape 'percy upload' uses for web projects — Percy renders
the baseline in the project's own browsers, so it pairs with the head
run's DOM snapshots by (name, browser, width).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seeding dispatches by project type: web projects seed rendered snapshots
(root DOM + image resource); app projects seed raw comparison uploads
(tag + tile), exactly how App Percy ingests screenshots. Exec seeding now
engages for app tokens too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shivanshu-07 Shivanshu-07 requested a review from a team as a code owner July 13, 2026 13:03
let found = [];

while (dir !== path.dirname(dir)) {
let modulesDir = path.join(dir, 'node_modules');
if (fs.existsSync(modulesDir)) {
for (let name of fs.readdirSync(modulesDir)) {
if (name === '@percy') {
for (let scoped of fs.readdirSync(path.join(modulesDir, name))) {
if (fs.existsSync(modulesDir)) {
for (let name of fs.readdirSync(modulesDir)) {
if (name === '@percy') {
for (let scoped of fs.readdirSync(path.join(modulesDir, name))) {
for (let name of fs.readdirSync(modulesDir)) {
if (name === '@percy') {
for (let scoped of fs.readdirSync(path.join(modulesDir, name))) {
found.push(path.join(modulesDir, name, scoped));
for (let name of fs.readdirSync(modulesDir)) {
if (name === '@percy') {
for (let scoped of fs.readdirSync(path.join(modulesDir, name))) {
found.push(path.join(modulesDir, name, scoped));
found.push(path.join(modulesDir, name, scoped));
}
} else if (name.startsWith('percy-')) {
found.push(path.join(modulesDir, name));
found.push(path.join(modulesDir, name, scoped));
}
} else if (name.startsWith('percy-')) {
found.push(path.join(modulesDir, name));
// none is installed (the overwhelmingly common case — one existsSync walk, negligible cost).
export async function findBaselineProvider({ cwd = process.cwd(), log } = {}) {
for (let pkgPath of findPercyPackages(cwd)) {
let pkgFile = path.join(pkgPath, 'package.json');
let providerPath = pkg['@percy/cli']?.baselineProvider;
if (!providerPath) continue;

let module = await import(url.pathToFileURL(path.join(pkgPath, providerPath)).href);
let providerPath = pkg['@percy/cli']?.baselineProvider;
if (!providerPath) continue;

let module = await import(url.pathToFileURL(path.join(pkgPath, providerPath)).href);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants