Recommended Free Tools
Use browser.wait with a callback that locates the element, reads its class attribute, and returns a promise resolving to true only when the required state exists. Capture the original class before the action if you need to prove that a transition occurred. For disappearance, decide whether the element must be removed from the DOM or merely become invisible; those are different conditions.
The core pattern: poll the element and return a Boolean promise
Protractor’s browser.wait repeatedly evaluates a condition until it returns a truthy value or the timeout expires. A condition can return a promise, so an asynchronous getAttribute('class') read fits naturally.
Locate the element with element(locator), then read its class attribute through the ElementFinder. Keep the callback limited to observation. Do not click buttons, submit forms, or mutate application state inside it, because the callback may run many times.
var target = element(by.css('.notice'));
browser.wait(function() {
return target.getAttribute('class').then(function(className) {
return /(^|s)is-ready(s|$)/.test(className || '');
});
}, 5000, 'Expected the notice to gain is-ready');
The timeout is in milliseconds. If the class token appears within five seconds, the wait resolves. Otherwise Protractor throws a timeout error containing the message you supplied.
#1 Best Overall
Wait for any class change
When the requirement is simply that the class value differ from its starting value, save the initial value and compare each later poll with it.
var target = element(by.css('.notice'));
var initialClass;
browser.wait(function() {
return target.getAttribute('class').then(function(className) {
if (initialClass === undefined) {
initialClass = className || '';
return false;
}
return (className || '') !== initialClass;
});
}, 5000, 'Expected the notice class to change');
This version records the first value observed by the wait. That is safe only when the action causing the transition cannot finish before the first poll. If the update might happen immediately, capture the value before triggering the action.
Capture the before-state before clicking
var target = element(by.css('.notice'));
var initialClass;
target.getAttribute('class').then(function(className) {
initialClass = className || '';
return element(by.buttonText('Start')).click();
}).then(function() {
return browser.wait(function() {
return target.getAttribute('class').then(function(className) {
return (className || '') !== initialClass;
});
}, 5000, 'Expected the notice class to change after Start');
});
Capturing first gives the comparison a real before-and-after boundary. Without it, a very fast transition can be observed only after it has completed, making the first poll look like the initial state.
Wait for a specific class to appear or disappear
A token-specific predicate communicates the intended UI state more precisely than “anything changed.” Match whitespace-delimited tokens rather than using indexOf; substring checks can mistake is-ready-large for is-ready.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
Class token appears
var target = element(by.css('.notice'));
browser.wait(function() {
return target.getAttribute('class').then(function(className) {
return /(^|s)is-ready(s|$)/.test(className || '');
});
}, 5000, 'Expected the notice to gain is-ready');
Class token is removed
var target = element(by.css('.notice'));
browser.wait(function() {
return target.getAttribute('class').then(function(className) {
return !/(^|s)is-loading(s|$)/.test(className || '');
});
}, 5000, 'Expected the notice to lose is-loading');
If the class attribute is missing, className || '' treats it as an empty class list. That makes a removed token satisfy the predicate, which is usually the desired interpretation.
Re-locate when the application replaces the node
Framework updates can remove an element and insert a new element at the same selector. A previously resolved WebElement may then refer to a detached node, producing a stale-element error. Build the ElementFinder inside the callback so every poll performs a fresh lookup.
browser.wait(function() {
return element(by.css('.notice')).getAttribute('class').then(function(className) {
return /(^|s)is-ready(s|$)/.test(className || '');
});
}, 5000, 'Expected the replacement notice to gain is-ready');
This does not make every stale-element race impossible, but it narrows the window in which a reference can become invalid. If replacement is itself the behavior under test, first wait for the old node to leave (or for the replacement condition your installed API supports), then locate the new node and assert its class.
“Disappear” has two meanings
Choose the assertion that matches the application contract. A hidden node can remain attached to the DOM, while a removed node cannot be queried as an existing element.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Element is removed from the DOM
Use a fresh lookup and isPresent(). The wait succeeds only when the locator no longer finds a node.
browser.wait(function() {
return element(by.css('.notice')).isPresent().then(function(present) {
return !present;
});
}, 5000, 'Expected the notice to be removed from the DOM');
This is the right check for applications that destroy a component after completion, dismiss a notification by removing it, or navigate to markup where the selector no longer exists.
Element is hidden or not visible
If the node remains in the DOM but should no longer be visible, use Protractor’s expected-condition API when it is exposed by the version pinned in your project.
var target = element(by.css('.notice'));
var EC = protractor.ExpectedConditions;
browser.wait(
EC.invisibilityOf(target),
5000,
'Expected the notice to become invisible'
);
Invisibility conditions generally succeed when the target is hidden or absent. Confirm the exact helper name and behavior in your installed Protractor/WebDriver version before adopting it. Do not substitute this check for a DOM-removal assertion when continued attachment matters; likewise, do not demand removal when the application intentionally keeps a hidden component for reuse.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #4
Choosing the right predicate
| Requirement | Predicate | Why use it |
|---|---|---|
| Any class transition | Current class differs from captured initial class | Useful when the particular destination class is not part of the contract |
| A class is added | Whitespace-token regular expression returns true | States the exact ready, active, or completed condition |
| A class is removed | Token regular expression returns false | Checks that a loading, disabled, or pending marker is gone |
| Node is destroyed | Fresh lookup followed by isPresent() equals false |
Verifies DOM absence rather than visual appearance |
| Node remains but is hidden | ExpectedConditions.invisibilityOf, if available |
Checks visibility behavior without requiring removal |
Reliable waits in real test suites
Use a bounded timeout and diagnostic message
Every wait should have an explicit upper bound and a message describing the expected state. A five-second timeout is only an example; choose a limit appropriate for the operation and your continuous-integration environment. Avoid an unbounded loop, which can leave a failed test hanging indefinitely.
Keep polling side-effect free
The callback may be evaluated repeatedly. Read attributes, presence, or visibility, and return a Boolean or a promise for one. Put the click or other state-changing operation before browser.wait.
Wait for the state your user needs
A class change may occur before images, data, or controls are usable. If the actual contract is “the button can be clicked,” wait for that state directly, such as an enabled class plus the relevant interaction check, rather than treating any class mutation as completion.
Use stable locators
Prefer a dedicated test attribute or a stable component selector when one exists. A selector tied to styling can change during a redesign and cause a test failure unrelated to behavior. If the selector must remain the same while the node is replaced, re-query it on every poll as shown above.
Best Value
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Timeout even though the page appears updated | The predicate expects the wrong class token, or the class is applied to a different node | Inspect the exact element and class string, then correct the locator or token expression |
| Timeout when waiting for removal | The application hides the element instead of removing it | Use an invisibility condition, or change the product behavior if DOM removal is required |
| Stale element reference | The framework replaced the node between lookup and attribute read | Create the ElementFinder inside the wait callback and avoid retaining resolved WebElements |
| “Any change” wait passes unexpectedly | The initial class was captured after the triggering action | Read the class before clicking or dispatching the event |
| Class check matches the wrong state | Substring matching treats one class name as part of another | Use a whitespace-delimited token expression such as /(^|s)token(s|$)/ |
| Expected-condition helper is undefined | The project’s pinned Protractor version exposes a different API | Check that installed version’s ExpectedConditions implementation and use a custom browser.wait predicate if necessary |
| Test is flaky only in CI | The timeout is too short for slower startup or the test races a node replacement | Use a bounded but realistic timeout, fresh lookups, and a predicate tied to the final state rather than an arbitrary delay |
Protractor’s maintenance status
These patterns are for maintaining an existing Protractor suite. The official Protractor repository was archived on July 29, 2024, and the project’s future-of-end-to-end-testing guidance encourages migration toward a modern framework-agnostic platform. The snippets above describe the documented WebDriverJS-style APIs; they are not a claim that any particular installed version has been executed here. For a new test suite, evaluate a maintained alternative and confirm its locator, wait, and visibility APIs before choosing a framework.
Or skip the browser setup
If your goal is to capture the page rather than exercise its interaction logic, ScreenshotNeo can return a screenshot or PDF from one HTTP request. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
See the ScreenshotNeo documentation for all options, including full-page and selector captures, device and retina settings, waits, custom CSS or JavaScript, request blocking, cookies and headers, geolocation, PDFs, caching, signed links, webhooks, bulk capture, and usage reporting.
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}`);
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free. Start at ScreenshotNeo’s free sign-up.
Frequently Asked Questions
Should a class wait assert the complete class attribute or one token?
Assert one token when other classes are allowed to change independently. Compare the complete string only when the exact class list is part of the component contract.
What should I do when a transition removes the selector before the final class appears?
Treat replacement as a two-stage condition: wait for the old locator to disappear, then locate the replacement and wait for its required class.
Can a CSS animation’s temporary class be a reliable test condition?
Only if that class represents a documented application state. Animation-specific markers can be timing details; prefer a semantic ready, completed, or hidden state when one exists.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

