Run Only the Playwright Tests Your Change Can Affect Developer Alexander Op built feature-shop, a Nuxt demo shop, to test a CI approach that runs only the Playwright end-to-end scenarios affected by a code change, using feature folders, @feature tags, and a dependency map in tools/impact.ts. In four pull requests against the same base commit, a checkout view change ran 4 of 14 browser tests, a search view change ran 5 of 14, a cart view change ran 10 of 14, and a shared CSS change ran all 14, with all runs passing in GitHub Actions. The setup requires Node 24 and pnpm 10, and shared styles, test infrastructure, or unknown files trigger the full suite. With AI writing more of our code, I keep coming back to the testing discussion. Are unit tests still useful? How much confidence do they give us when an agent writes both the implementation and the tests? I still want unit tests for business rules. But I also want to open the application, add something to a cart, and complete a checkout. An end-to-end test checks whether those pieces work together, through the interface someone uses. The problem is the waiting. If I change checkout validation, do I need to run every search scenario before getting feedback? Last week I built feature-shop https://github.com/alexanderop/feature-shop , a small Nuxt shop, to try a different approach: run the E2E tests for the changed feature and the features that depend on it. Feature folders give us a starting point In the shop, search, product details, cart, and checkout each have their own feature folder. The browser tests follow the same structure. app/features/ search/ product-detail/ cart/ checkout/ tests/e2e/features/ search/ product-detail/ cart/ checkout/ That makes ownership easier to describe. A change in app/features/checkout/ belongs to checkout. The selector can find the scenarios tagged with @checkout and run them. Each scenario also has a stable ID. This one checks the result a customer cares about: @checkout Feature: Simulated checkout @id:checkout-success Scenario: Successful checkout clears the cart Given my cart contains 1 backpacks When I open checkout And I enter valid customer details And I choose an approved payment And I place my order Then my order is confirmed And my receipt total is "40.00" When I open my cart Then my cart is empty I use Playwright-BDD to connect these sentences to Playwright fixtures and page objects. You can use the same selection idea with plain Playwright tests and tags. A cart change can break checkout Folder names alone aren’t enough. Checkout uses the cart. Product details let you add an item to it. If I change cart behavior, I need to test those consumers too. In tools/impact.ts https://github.com/alexanderop/feature-shop/blob/2bb25da27c6481e52b8e9f19823ae4659b392453/tools/impact.ts , I describe the file ownership and dependencies. The selector follows consumers, including indirect ones, until it has the affected set. The purchase journey crosses feature boundaries, so I tag it with all the features it covers. A checkout change still runs that complete journey. Shared styles, dependencies, test infrastructure, and unknown files trigger the full suite. So does an unavailable Git comparison. Documentation-only changes select no browser scenarios, but CI still runs the static checks and unit tests. This depends on keeping the ownership map and scenario tags accurate. A feature folder doesn’t prove isolation. Review dependencies when you add imports, shared state, or API interactions. What happened in GitHub Actions I opened four small pull requests against the same base commit. Each changed one application file. | Change | Browser tests executed | Result | |---|---|---| | Checkout view | 4 / 14 | Passed | | Search view | 5 / 14 | Passed | | Cart view | 10 / 14 | Passed | | Shared CSS | 14 / 14 | Passed | The verification record https://github.com/alexanderop/feature-shop/blob/2bb25da27c6481e52b8e9f19823ae4659b392453/docs/github-verification.md links to each PR and Actions run. It records comparisons between selected scenario IDs and the tests Playwright executed. These are test counts, not a speed benchmark. Installing dependencies, building the application, and starting the browser still cost time. Try the same setup Use Node 24 and pnpm 10. These commands check out the published version behind this post, including its tests and workflow: git clone https://github.com/alexanderop/feature-shop.git cd feature-shop git checkout -b try-affected-tests 2bb25da27c6481e52b8e9f19823ae4659b392453 pnpm install --frozen-lockfile pnpm exec playwright install chromium pnpm check pnpm verify run --all Leave port 3401 free. The runner builds and starts the shop for you. On Linux, use pnpm exec playwright install --with-deps chromium to install browser system dependencies too. Edit app/features/checkout/CheckoutView.vue , then inspect the selection before running it: pnpm verify plan --affected pnpm verify run --affected Expect the three checkout scenarios and the purchase journey. The default comparison includes uncommitted changes against HEAD . After committing your edits, use --base main to include your branch changes. For GitHub Actions, fork the repository, enable Actions if GitHub prompts you, and open a PR against your fork’s main branch . The existing complete workflow https://github.com/alexanderop/feature-shop/blob/2bb25da27c6481e52b8e9f19823ae4659b392453/.github/workflows/verify.yml contains dependency installation, checks, the production build, and report uploads. Its selection step is: Excerpt from .github/workflows/verify.yml - name: Run affected scenarios if: github.event name == 'pull request' env: VERIFY SKIP BUILD: '1' VERIFY BASE: ${{ github.event.pull request.base.sha }} VERIFY HEAD: ${{ github.event.pull request.head.sha }} run: pnpm --silent verify run --affected --base "$VERIFY BASE" --head "$VERIFY HEAD" --json pnpm verify is this project’s CLI, not a built-in Playwright command. It compares changes from the Git merge base, selects scenario IDs, and passes them to Playwright’s --grep filter https://playwright.dev/docs/test-cli reference . The workflow fetches Git history with fetch-depth: 0 and tests the PR head commit. It runs all scenarios on main, on a weekly schedule, and on manual runs. To adapt this to your own app, copy the selector and runner with their supporting files in tools/ , then map your paths, dependencies, and scenarios. Adapt the Playwright configuration and workflow to your application’s start command. Copying the YAML excerpt alone won’t create that setup. Keep checking the selection Set the repository Actions variable SHADOW FULL SUITE=true while evaluating this approach. Successful selective runs will then continue into a full run, so you can look for failures outside the selected tests. In this published workflow, a failed selective step stops that later step. A full run on main can catch missed dependencies after merging. It cannot protect the PR before merging. Keep the full pre-merge run if you need that assurance. This fits how I think about a modern quality pipeline https://alexop.dev/posts/modern-frontend-quality-pipeline/ A Modern Quality Pipeline and Testing Strategy for Frontend ProjectsA short, framework-agnostic concept of what a modern quality pipeline and testing strategy look like for any JavaScript or TypeScript frontend project.: give yourself and your agent useful feedback at a cost you can afford. I’ve covered what a passing browser test should prove https://alexop.dev/posts/what-should-a-green-test-prove-vitest-browser-mode/ What Should a Green Test Prove? Vue Testing with Vitest Browser ModeA CSS bug passes in jsdom but blocks a real purchase. Build Vue tests around behavior, accessibility, and visual regression with Vitest Browser Mode. and where to put code so you can test business rules separately https://alexop.dev/posts/hexagonal-architecture-vue/ Hexagonal Architecture in Vue: Where to Put Your Code and WhyLearn what belongs in Vue components, composables, the application core, and adapters. Use ports and adapters to make rules easier to test and changes easier to contain. in earlier posts. I still want those unit tests. I also want browser tests that tell me whether someone can complete a purchase. With clear feature boundaries, I can run the relevant journeys while working and keep broader regression checks in the pipeline.