ScreenshotNeo

BlogGuides

How to Improve Front-End Testing in 2019

A practical 2019 guide to choosing browser, component, unit, and API tests around user-visible risks, with advice on selectors, reliability, and accessibility.

By the ScreenshotNeo team4 October 202610 min read

Improve front-end testing by matching each test to the question it can answer best. In 2019, one approachable way to start was to cover a few recognizable user journeys from the top of the test pyramid, then add faster UI integration, component, and unit tests where browser-level tests became slow, hard to diagnose, or awkward for edge cases. Use API tests for service contracts and test data setup. Keep browser tests focused, assert on what users can observe, and treat accessibility as both an automated and manual concern.

This is a historical guide to advice published in 2019. The practical principles remain useful, but the current Cypress and Testing Library guidance cited below is labeled separately; check current documentation for version-specific APIs.

1. Start with a small set of user-visible risks

Write down the few workflows whose failure would prevent a user from completing an important task. Examples might include signing in, submitting a core form, or completing checkout. Choose the test level that can detect each risk with a useful signal and a diagnosis your team can act on.

  1. List important user outcomes, not implementation details.
  2. Choose a small number of browser tests for workflows that must work across the application.
  3. Add isolated UI tests for states and variations that are cumbersome to reach through the browser.
  4. Use API checks for endpoint contracts, permissions, errors, pagination, and repeatable data setup.
  5. Use unit tests for small, separable logic when direct feedback or many input cases matter.
  6. Review failures and remove tests that duplicate expensive coverage without adding a distinct signal.

Stefano Magni’s October 10, 2019 Cypress guest post framed its “top to bottom” approach as a way to engage developers in testing, rather than a universal ranking of best practices. Magni wrote: “This post is not about best or bad practices (take a look at the end of the post for a long list of resources), this post is about engaging new front-end developers profitably in the testing world.” Read [the original 2019 article](https://www.cypress.io/blog/guest-post-new-to-front-end-testing-start-from-the-top-of-the-pyramid) in that context.

2. Choose test levels by the question they answer

Test type Best question Strength Limit and cost
End-to-end (E2E) Can a user complete a critical flow across the browser, frontend, backend, and relevant integrations? Exercises the application as a cohesive system and follows realistic user activity. Needs more infrastructure and setup; network and backend performance can affect results; failures may be harder to isolate and maintain.
UI integration Does a page or group of components render and respond correctly with controlled service responses? Can cover user-visible behavior without requiring a working backend for every run. Stubbed responses cannot prove the real service integration works.
Component Does this isolated component behave correctly across its states and inputs? Focused feedback without external systems; useful for detailed variations. Passing component tests do not prove app layers work together.
API Does an HTTP endpoint honor its contract, permissions, and error behavior? Direct and precise for service behavior; can prepare state more efficiently than repeated UI flows. Does not establish that the interface renders or behaves correctly.
Unit Does this small piece of separable logic return the right result for its inputs? Direct feedback and broad edge-case coverage for pure logic. Cannot establish that UI, services, and application wiring work together.
Accessibility Are known accessibility rules violated, and can users operate the interface? Automated scans can flag known issues; behavior assertions can check keyboard interactions and accessible names. No automated scan or semantic locator proves an interface is fully accessible; manual checks remain necessary.

Current Cypress documentation also groups testing into E2E, component, API, and accessibility work, emphasizing their different coverage and tradeoffs. Its descriptions are current guidance, not claims about what every team did in 2019. See [Cypress testing types](https://docs.cypress.io/app/core-concepts/testing-types).

Should you start with end-to-end tests or unit tests?

If developers are discouraged by a large pile of low-level tests whose value is unclear, a small user-facing test can make the purpose of testing concrete. Then move down to faster, narrower tests for the cases that need them. This is a learning and sequencing choice, not a reason to abandon unit tests or invert every team’s test suite.

Magni distinguished full E2E tests, which need a working backend and database and can be affected by network and backend performance, from UI integration tests with stubbed AJAX responses. He wrote that UI integration tests “are more feasible because all the AJAX requests are stubbed (replaced with static JSON files) so you do not need a working back-end and they are fast, reliable, predictable, and they allow you to work independently.” Those adjectives describe his experience and framing, not a measured guarantee. Start with UI integration tests when controlled responses fit the behavior under test; retain E2E coverage for risks that depend on the real system working together.

3. Write tests around behavior users can observe

Prefer assertions about visible outcomes: the confirmation appears, an error is explained, a button becomes available after valid input, or a form section changes when a choice changes. Avoid coupling a test to private component state or an internal function when the user-facing result is the contract.

React Testing Library describes its purpose as testing React components through DOM nodes in a way that resembles user use, and its guiding principle is: “The more your tests resemble the way your software is used, the more confidence they can give you.” It is a utility layer, not a test runner or a required framework. See the [React Testing Library introduction](https://testing-library.com/docs/react-testing-library/intro/).

Choose selectors that match what should break the test

  • Use a role and accessible name when the control’s semantic purpose is part of the behavior being tested.
  • Use a label for form controls and assert the associated user-facing outcome.
  • Use visible text when a wording change should require the test to be reviewed.
  • Use a stable data attribute when incidental text or styling can change without changing the behavior under test.
  • Avoid selectors tied to CSS classes or DOM structure unless that structure itself is the requirement.

Current [Cypress best practices](https://docs.cypress.io/app/core-concepts/best-practices) recommend selectors based on the intended contract, including data attributes where selection should be independent of CSS or JavaScript changes. A role-based selector can make a test clearer, but does not alone prove accessibility.

4. Keep browser tests focused and reliable

  • Cover a few critical journeys rather than replaying every variation through a costly full-stack path.
  • Control initial state and isolate tests so one test does not depend on another’s order or side effects.
  • Stub network responses for UI behavior that does not require a live service; keep separate API or E2E checks for the real contract and integration.
  • Wait for a meaningful condition, such as a result becoming visible, instead of sleeping for an arbitrary duration.
  • Make assertions close to the action that could fail so the failure points to a specific expectation.
  • Run a useful subset during development and the intended full suite in continuous integration.

Fixed sleeps waste time when the app is ready early and still fail when it is slower than the chosen delay. Prefer the test framework’s condition-based waiting and assert the resulting state. Framework retry and waiting behavior varies by tool and version, so consult the relevant current docs rather than assuming a particular API.

5. Treat accessibility as a testing layer

Add automated accessibility scans where they fit, and write explicit assertions for important accessible names, states, and keyboard interactions. Then manually inspect high-impact journeys with keyboard use and assistive technology where appropriate. Automated checks can catch known rule violations; they cannot prove that the experience is accessible to everyone. A test that locates a button by role only shows that the locator found a role and name, not that the full interaction is usable.

6. Use screenshots to inspect visual regressions

Behavior tests answer whether controls and flows work. Screenshots can help review whether the rendered page looks as expected, especially after CSS, responsive layout, or content changes. A screenshot is evidence of appearance at a particular viewport and state; it does not by itself verify semantics, keyboard operation, or service behavior.

For a repeatable visual check, make the route, viewport, data, fonts, animation state, and consent state deterministic before capturing. Compare like with like: a full-page capture is useful for page layout, while a selector capture is better when a single component matters. Record the browser conditions needed to interpret the image and investigate differences instead of treating every pixel change as a defect.

7. Do-it-yourself screenshot capture

For a local browser workflow, use your existing browser automation framework to navigate to a deterministic test route and save a screenshot after the expected state appears. The short example below uses Playwright’s Node.js API; install the current Playwright package and browser as described in its official documentation before running it.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
try {
  await page.goto('http://127.0.0.1:3000/checkout', { waitUntil: 'domcontentloaded' });
  await page.getByRole('heading', { name: 'Checkout' }).waitFor();
  await page.screenshot({ path: 'checkout.png', fullPage: true });
} finally {
  await browser.close();
}

For stability, make the page’s test data repeatable, wait for an observable readiness condition, disable or settle animations where appropriate, and avoid capturing while content is still shifting. Use the browser runner for interactive assertions as well as images; a saved image alone is not a test of the full flow.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns a PNG, JPEG, WebP, or PDF. For a saved WebP screenshot:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. It can accept cookie and consent banners as a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with response headers indicating the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Start with 1,000 free screenshots a month, no card required.

9. Configuration and practical considerations

For automated browser captures, keep the conditions that affect output explicit: target URL, viewport, browser state, test data, wait condition, and whether the capture is full-page or limited to an element. For screenshot API workflows, ScreenshotNeo supports full-page capture with lazy images loaded, CSS selector element capture, dark mode, 12 device presets or a custom viewport, retina scale, PDF paper size/margins/orientation/page ranges, HTML/CSS to image, custom CSS and JavaScript, click-before-capture, hide selectors, selector/delay/network-idle waits, request and resource blocking, headers, cookies, user agent and Authorization, timezone and geolocation, transparency, resizing, configurable TTL caching, signed image links, async jobs with signed webhooks, bulk capture up to 100 URLs per call, usage API, and OpenAPI specification. Other screenshot APIs’ parameter names also work to make migration easier. Consult the docs for exact parameter names and formats.

Performance, reliability, and cost

  • Performance: Browser-based E2E paths include navigation and application work; use narrow component and unit tests for rapid iteration and many edge cases. Avoid repeating the same expensive setup across layers without a reason.
  • Reliability: Deterministic data, isolated state, condition-based waits, and stable selectors reduce avoidable variation. External services and network timing remain sources of uncertainty for full E2E coverage.
  • Cost: Consider execution time, CI resources, test maintenance, and failure diagnosis together. The cheapest test to run is not useful if it cannot exercise the behavior at risk. ScreenshotNeo pricing is Free for 1,000 shots/month, Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free, and every feature is on every plan.

10. Troubleshooting

Symptom Likely cause Fix
Browser test passes locally but fails in CI Different data, environment, timing, browser, or external service state. Control test data and dependencies, capture useful failure artifacts, and wait for the expected condition rather than a fixed delay.
Test fails after a harmless CSS or markup change Selector is coupled to styling or incidental DOM structure. Choose a role, label, contract-relevant text, or stable data attribute based on what the test should protect.
UI test needs a live backend just to show a component state Test scope includes unnecessary service dependencies. Stub the relevant request for the UI behavior test; retain API/E2E tests to check the real service contract.
Suite is slow and failures are hard to diagnose Too many scenarios run through full browser and backend paths. Keep E2E coverage to critical cross-system journeys and move narrow state variations to component, UI integration, or unit tests.
Screenshot differs between runs Unsettled fonts, animation, asynchronous content, changing data, or viewport variation. Fix the route, data, viewport, and readiness condition; settle animations and content before capture.
Accessibility scan is clean but users still struggle Automated rules cover only detectable issue classes. Manually test keyboard paths and important flows with assistive technology; add explicit behavior assertions.
Screenshot API request returns an unexpected result Invalid credentials or URL, page failure, bot check, timeout, or configuration mismatch. Check request parameters and the response’s X-Page-Verdict and X-Billed headers; consult the API docs for option formats.

11. Frequently asked questions

Does a top-to-bottom approach mean unit tests are bad?

No. It describes one approachable order for building confidence. Unit tests remain useful for isolated logic and cases where direct feedback matters.

Can API tests replace browser tests?

No. API tests establish endpoint behavior, not whether the interface renders or a user can complete a flow.

Does a screenshot test prove accessibility?

No. It records appearance. Accessibility needs semantic and interaction checks, automated scans, and manual review.

Was Cypress the required tool in the 2019 advice?

No. Cypress provided the historical articles in the dossier, but the advice is about choosing tests by purpose and can be applied with other tools.