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, 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, 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 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 contains dependency installation, checks, the production build, and report uploads. Its selection step is:
- 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.
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 pipelineA 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 proveWhat 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 separatelyHexagonal 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.