Stop waiting on the one-shot added-to-cart notice - #67
Merged
Conversation
addProductToCart waited for WooCommerce's "product added to cart" notice, a one-shot session notice: it is cleared on print and is lost whenever anything rewrites the WooCommerce session between the add-to-cart request and the render — an async loopback, a persistent object cache, a plugin touching the session. The wait then timed out on runs where the product had in fact been added, one or two different tests per run, always green when re-run in isolation. The wait is now gone rather than retargeted. There is nothing left to synchronize: amOnPage returns only once the add-to-cart request has been served, so the cart is already populated when the method returns. The notice was picked in the first place because it is the only element guaranteed to exist on every page a store can land on after an add — cart state is only rendered on the cart page, and reaching it would mean either navigating on every add or branching on the store's configuration. Neither is warranted: 19 of the package's own call sites go straight to the checkout page and never look at the cart, so both options spend a page load, and the branch would additionally turn cartPageSlug into a value that breaks every add when it is wrong. The method is therefore agnostic again to the "Add to cart behaviour" setting and to the woocommerce_add_to_cart_redirect filter, and tests navigate explicitly before asserting on cart contents, as they already did. PRODUCT_ADDED_TO_CART_MESSAGE_SELECTOR loses its only consumer and is removed from the cart page object.
edpittol
self-requested a review
September 2, 2026 17:56
edpittol
approved these changes
Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #66.
Problem
CartMethods::addProductToCart()waited for WooCommerce's "product added to cart" notice. That notice is a one-shot session notice: it lives inwc_notices, is rendered once, and is cleared on print. Anything that rewrites the WooCommerce session between the add-to-cart request and the render — an async loopback, a persistent object cache, a plugin touching the session — destroys the wait target while the add itself succeeds, because the cart is restored from the persistent-cart user meta and notices have no such fallback. Result: intermittentTimeoutException, a different test or two per run, always green when re-run in isolation. Full diagnosis and field evidence in #66.Change
The wait is removed rather than retargeted:
There is nothing left to synchronize.
amOnPage()returns only once the add-to-cart request has been served, so the cart is already populated when the method returns — the wait was never synchronization, it was an implicit assertion.PRODUCT_ADDED_TO_CART_MESSAGE_SELECTORloses its only consumer and is removed fromCartPageObject.Why not wait on the cart item instead
That was the fix suggested in the issue, and it needs the cart page:
CART_ITEM_SELECTORis rendered nowhere else. Reaching it means either navigating on every add, or branching on whether the store already redirected there. Neither earns its cost:amOnCheckoutPage()and never look at the cart. Both options spend a page load on a page the test abandons on the next line.cartPageSlug, which promotes that config value from "only matters if you callamOnCartPage()" to "breaks every add when wrong" — and it degrades silently in subdirectory installs, where the paths never match.CART_ITEM_SELECTORonly proves the cart is non-empty, so on a second consecutive add it passes on the first item's markup and proves nothing.Keeping the method to the add alone leaves it agnostic to the store's "Add to cart behaviour" setting and to the
woocommerce_add_to_cart_redirectfilter, and keeps the browser layer free of store shape (ADR-0008).Trade-off
A failed add (out of stock, invalid product) now surfaces at the next
see*rather than insideaddProductToCart(). No DOM element is reliably present on every landing page a store can choose, which is exactly why the fragile notice was picked originally. Tests that want the guarantee assert it explicitly —amOnCartPage()thenseeCartTotalQuantity()— which is a stronger check than the generic wait ever was.Consumer impact
Tests that assert on cart contents must navigate first. This is what the suites already do; the package's own Cests are unchanged by this PR.
Verification
composer check— composer validate, PHPStan level max oversrcandtests, PHPCS: clean.codecept run unit— 60/60.codecept run acceptance— 198 tests, 427 assertions, no failures.Ran against a locally built runner image; the pre-push hook was bypassed because
ghcr.io/aztecweb/aztecweb-wp-browser-runnerrequires authentication in this environment, so the suites above were run by hand instead. Unrelated, but noticed while doing it: the hook's file-to-Cest map still uses pre-namespace paths (src/Method/CartMethods.php), so no changed source file maps to a Cest any more and it silently falls through to bootstrap only.