[White Paper]

Challenges of testing Single Page Applications with Selenium

Last updated: October 2026

Single-page web applications are popular for their ability to improve the user experience. Except, test automation for Single-page apps can be difficult and time-consuming. We’ll discuss how you can have a steady quality control without burning time and effort.

With the benefits of having a thick client structure, ability of the Single-page app to mimic a native app is too dynamic for an unadapted Selenium WebDriver tests. The problem is that Single-page apps have many components that are asynchronous from each other. While Selenium’s automation framework implicitly executes for static objects, the appearance of objects in Single-page apps is unpredictable.


    Quick answer

    Single-page applications (SPAs) built with frameworks such as React, Angular or Vue are hard to test with Selenium because the page never fully reloads: content appears asynchronously, elements are re-rendered and replaced, and IDs are often generated. Tests that rely on page-load waits, fixed sleeps or brittle locators become flaky. The fixes are explicit waits for the state you need, stable test-specific locators, re-finding elements after re-renders, and handling shadow DOM and iframes deliberately.

    Why single-page apps break Selenium tests

    • No page loads to wait for. Navigation happens in JavaScript, so Selenium’s page-load wait returns before the new view is ready.
    • Asynchronous rendering. Data arrives from APIs at unpredictable times, so an element may not exist yet, or may exist but still be empty.
    • Re-rendered elements. Frameworks replace DOM nodes when state changes, so a reference found a moment ago throws a StaleElementReferenceException.
    • Generated IDs and classes. Component libraries and CSS-in-JS produce attributes that change between builds.
    • Shadow DOM and iframes. Web components hide their internals in shadow roots, and embedded widgets live in iframes that normal locators cannot see.
    • Lazy loading and infinite scroll. Content only appears after scrolling or other interactions.

    Practical fixes in Selenium

    • Use explicit waits, never fixed sleeps. Wait for the condition the next step needs, for example with WebDriverWait and ExpectedConditions.elementToBeClickable or visibilityOfElementLocated.
    • Wait for state, not time. After a route change, wait for the URL or for a heading that only the new view has.
    • Add stable test attributes. Ask developers for data-test or data-testid attributes and locate by them instead of generated IDs or long XPath.
    • Re-find elements after actions. Locate the element again after anything that changes state, or wrap interactions in a helper that retries on stale references.
    • Handle shadow DOM explicitly. Selenium 4 can reach into open shadow roots with getShadowRoot(); switch into iframes with switchTo().frame() before locating elements inside them.
    • Scroll before you look. For lazy-loaded lists, scroll the element into view and then wait for it.

    How CloudQA handles single-page apps

    CloudQA records tests in a real browser and waits automatically for page loads and AJAX activity, so you do not have to add waits by hand. Its recorder captures resilient locators, and tests handle shadow DOM and iframes, which are common in modern single-page applications. Tests run in the cloud on Chrome, Firefox and Edge, on a schedule or from your CI/CD pipeline. See automated regression testing with CloudQA.

    Frequently Asked Questions

    Usually because the test acts before the view is ready or uses an element that the framework has already re-rendered. Explicit waits for the right condition and re-finding elements fix most of these failures.

    No. Fixed sleeps make tests slow and still flaky. Use explicit waits that continue as soon as the condition is met.

    Yes. Selenium 4 can access open shadow roots through getShadowRoot(); closed shadow roots are not accessible from the outside.