[tests] Demote 4 Products block E2E titles to Jest and PHPUnit - #68667
[tests] Demote 4 Products block E2E titles to Jest and PHPUnit#68667vladolaru wants to merge 2 commits into
Conversation
The Products block is the deprecated `core/query` variation registered under the `woocommerce/product-query` namespace. Its browser spec ran seven Playwright identities, and four of them spent a real page load proving contracts that never needed one. Three of those four were archive-route parity checks that scraped two title lists and asserted set equality. That assertion passes on two matching empty arrays and says nothing about ordering. The fourth repeated the add-to-cart journey character for character against a published post instead of the archive template, so its only real delta was the query mode. This adds the two lower-layer owners the split needs. A Jest suite covers `useAllowedControls`, the hook deciding which inspector controls a Products block offers, across its inherited, re-rendered and Post Editor branches. A PHPUnit method covers `ProductQuery::add_iapi_context`, the per-item Interactivity wiring the add-to-cart button depends on, asserting the exact namespace, key and decoded context on valid loop items and proving three kinds of invalid item are skipped. The retained Product Catalog title is rewritten rather than removed. It now clicks through the Product Collection upgrade and asserts the migrated query before saving and again after a reload, with cardinality floors on both sides, which closes the old hole of two matching singletons. The owner for the three archive-route titles is `RouteContextParityTest::test_product_collection_and_product_query_match_classic_route`. Its Products arm was written by this batch's slice commit but lives in a file batch 020 carries whole, so it ships in that pull request and is not yet on trunk. This branch should land after it. Consolidates the mega-branch slices: - Slice 069: test(blocks): Reduce Products block browser coverage Three later refinements to the retained parity title are folded in with it: "Verify Product Collection query parity", "Require multi-product query parity", and "Verify inherited collection parity". Refs TESTOPS-234 Refs #68046 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 58 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Test using WordPress PlaygroundThe changes in this pull request can be previewed and tested using a WordPress Playground instance. Test this pull request with WordPress Playground. Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a commit that changes plugin code. |
|
Size Change: 0 B 🆕 Total Size: 0 B |
Submission Review Guidelines:
Changes proposed in this Pull Request:
The Products block — the deprecated
core/queryvariation registered under thewoocommerce/product-querynamespace — spent seven Playwright identities on contracts that do not need a browser. This moves those contracts down a layer and keeps three browser journeys.Refs TESTOPS-234
Part of the #68046 split.
Six files, 212 insertions and 73 deletions. No production code changes.
products.block_theme.spec.tsruntime titlesProductQuery.phpPHPUnit methodsproduct-query/test/inspector-controls.tsxJest casesPlease merge #68618 before this one. The reason is in the next section, and it is about which pull request carries a test rather than about risk to coverage.
Read this before the table: one absorber ships in a sibling pull request
Three of the four removed titles are archive-route parity checks, and their named replacement is
RouteContextParityTest::test_product_collection_and_product_query_match_classic_route. That method's Products arm was written by this batch's slice commit, but it lands inside a file that #68618 carries whole, so it travels with that pull request. It is not ontrunkand it is not in this diff —git cat-file -e origin/trunk:plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/RouteContextParityTest.phpfails today. Anyone grepping this diff for that class will not find it, which is why it is stated here rather than left in a table cell.This is a citation-integrity problem, not a coverage cliff, and the difference matters if #68618 stalls:
product-collection.block_theme.spec.tsontrunkalready runs a parity loop over Product Category (/product-category/music/), Product Tag (/product-tag/recommended/), Product Catalog (/shop/) and Product Search Results (/?s=shirt&post_type=product), assertingtoHaveCount( expectedProductsCount )and then name equality against the classic loop. That is a cardinality floor the removed Products titles never had — they compared two scraped arrays withtoEqual, which passes on two empty arrays.If #68618 stalls, this pull request is still mergeable provided the table below is amended to name trunk's Product Collection parity titles as the interim route owner.
Removed and rewritten titles
core/query Block › product button should add product to the cart when not inheriting query from templatetests/php/src/Blocks/BlockTypes/ProductQuery.php::test_add_iapi_context_updates_only_valid_product_loop_items(in this PR) owns the per-item Interactivity wiring the button depends on: the exactwoocommerce/productsnamespace, theproduct-item-{id}key and the decoded{ productId, variationId: null }context, proven on real products and proven absent on three kinds of invalid loop iteminherit: falseon a published post hydrates and completes a real add-to-cart click. The PHP owner asserts the context attributes that drive the button, not the click, the1 in cartlabel or theView cartlink. The retained inheriting-query title keeps that journey character-for-character, but on the archive route only, so the second query mode has no end-to-end ownerProduct Category template › Products block matches with classic template blockRouteContextParityTest::…match_classic_route, provider rowproduct category, expected[ 'Parity Shirt B', 'Parity Shirt A' ]— ships in #68618, not here. Interim browser owner on trunk:product-collection.block_theme.spec.ts›Product Category template›Product Collection block matches with classic template blockdo_blocksagainst an in-process query, so template authoring and URL routing are not covered by itProduct Tag template › Products block matches with classic template blockproduct tag, expected[ 'Parity Shirt C', 'Parity Shirt A' ]— ships in #68618. Same interim browser owner on trunkProduct Search Results template › Products block matches with classic template blockproduct search, expected[ 'Parity Shirt B', 'Parity Shirt C', 'Parity Shirt A' ]— ships in #68618. Same interim browser owner on trunkProduct Catalog template › Products block matches with classic template blockProduct Collection matches with classic template block, which stays in the browser/shop/in the retained add-to-cart title, so its catalog render keeps a browser owner, but its ordered parity with the classic loop on that route now has no owner at any layer:RouteContextParityTest's provider has category, tag and search rows and noarchive-productrowThe removed Product Tag title never tested a Product Tag template. The base loop hard-codes
templateName: 'Products by Category'inside the shared body, so bothneedsCreationrows created a category template; the Tag iteration then visited/product-tag/recommended/and compared two arrays that were empty. Worth knowing before treating its removal as a loss.Relocated to Jest
useAllowedControlsdecides which inspector controls the block offers. Three cases: the inherited Site Editor branch returns onlywooInherit; re-rendering withinherit: falserestores the full list, which proves the hook reacts to the attribute rather than caching its first branch; the Post Editor branch removeswooInheritand keeps everything else.The retained wiring test
Three titles survive, all in this batch's own spec, and
--listshows exactly these three:core/query Block › when Inherits Query From Template other options are hidden, show up otherwise— inserts the block in the Site Editor and reads the real inspector.core/query Block › product button should add product to the cart when inheriting query from template— the only surviving title anywhere that renders this block on a front end and buys from it. It carries the block's entire frontend contract.Product Catalog template › Product Collection matches with classic template block— the upgrade path, asserting the migrated query'sisProductCollectionBlock,inheritandperPagebefore the save and again after a reload, with cardinality floors on both sides.One more lives outside this PR:
product-collection.block_theme.spec.ts›Can be migrated to from Products (Deprecated) blockis, after this change, the block's only Post Editor coverage.Things a reviewer should not have to find
Everything in this list is left exactly as the migration branch wrote it, because this pull request moves tests rather than improving them. They are listed so nobody has to rediscover them.
ProductCollection/Utils.php::prepare_and_execute_query, not a Products one. That follows from the title now clicking through the upgrade: once migrated, the frontend query belongs to Product Collection.insertProductsQuery'sinheritoption is now dead. Its only{ inherit: false }caller was the removed title. The parameter is kept as the migration branch has it.products.block_theme.spec.ts:138-140. The post-reload read waits on the canvas body, which can resolve before the editor store has parsed, and the?? {}fallback inutils.ts:29turns that into a hard failure rather than a retry. It passed three times here at--retries=0 --workers=1, and a fourth run cleared the same window before failing where its mutation aimed. The alternative is adding a wait, which this campaign does not do.@wordpress/datamock ignores the store it is asked for. The fakeselectreturns the same variation whatever key it receives, soselect( WP_BLOCKS_STORE ).getActiveBlockVariation( QUERY_LOOP_ID, attributes )would behave identically with the wrong store, withoutQUERY_LOOP_ID, or without forwardingattributes— mutations to any of those three would survive this suite. What the three cases do assert isuseAllowedControls' branching, and all three are mutation-killed on exactly that.test_add_iapi_context_updates_only_valid_product_loop_itemsis@runInSeparateProcesswith@preserveGlobalState disabled, which is unusual intests/php. It is correct here:ProductsStoreholds a static product cache and the method writeswp_interactivity_state(), and the test-case transaction rollback resets neither.throwafter anassertIsString. PHPUnit has already failed the test by the time thethrowcould run, so it is dead defensive code rather than a guard.Screenshots or screen recordings:
Not applicable. Test-only change.
How to test the changes in this Pull Request:
pnpm install --frozen-lockfilefrom the repository root.pnpm --filter=@woocommerce/block-library test:js --runInBand assets/js/blocks/product-query/test/inspector-controls.tsxExpect
Tests: 3 passed, 3 total.pnpm --filter=@woocommerce/plugin-woocommerce env:test.pnpm --filter=@woocommerce/plugin-woocommerce test:php:env -- --testsuite=wc-phpunit-main --filter 'test_add_iapi_context_updates_only_valid_product_loop_items'. ExpectOK (1 test, 20 assertions).plugins/woocommerce/src/Blocks/BlockTypes/ProductQuery.php, inadd_iapi_context(), change'variationId' => null,to'variationId' => 0,. Re-run step 4 and expect a failure on the decoded context array. Revert.if ( ! $product_id || 'product' !== get_post_type( $product_id ) ) {toif ( ! $product_id ) {. Re-run step 4 and expectFailed asserting that 'woocommerce/products' is null— the non-product list item now gets annotated. Revert.plugins/woocommerce/client/blocks/assets/js/blocks/product-query/utils.tsx, replace the Site Editor ternary's true branchcontrols.filter( ( control ) => control === 'wooInherit' )withcontrols. Re-run step 2 and expect the inherited-query case to fail withexpected [wooInherit], received [wooInherit, onSale]. Revert. No rebuild is needed; Jest transpiles the source directly.pnpm --filter=@woocommerce/plugin-woocommerce env:e2e, thenpnpm --filter=@woocommerce/plugin-woocommerce env:start:blocks. The second command is required, not optional — it seeds the store (products, options and editor preferences) that the specs assume. The admin storage state, the database snapshot and the block theme activation all come from the Playwrightblocks setupproject, which step 9 runs because it passes no--no-deps.pnpm --filter=@woocommerce/plugin-woocommerce test:e2e:with-env default --project=blocks-chromium --retries=0 --workers=1 tests/e2e/tests/blocks/products/products.block_theme.spec.ts. Expect4 passed, which is the three titles plus the setup project.plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Utils.php, immediately after$query = clone $wp_query;, add$query->posts = array_slice( $query->posts, 0, 1 );and$query->post_count = 1;. Re-run step 9 and expect a failure atexpect( productCollectionProducts.length ).toBeGreaterThan( 1 )— and note that the classic-side floor on the line above passes first, which is what proves only the collection's cloned query was truncated. Revert.Testing that has already taken place:
Everything below ran on a local WordPress with retries disabled and one worker.
trunkwas at7cda01098f.8fe7a01548, verified by blob hash. Trunk drift on all four paths is empty since the diff base, so the change applies to trunk unmodified and nothing had to be re-derived.OK (1 test, 20 assertions)and each Jest command reports2 skipped, 1 passed, 3 total— both the exact green identities the matrix recorded. The PHPUnit filter was checked to have executed a real test rather than matching nothing, which also exits 0.--listshows exactly the three titles above; the run passes4 passedat--retries=0 --workers=1.data-wp-keyprefix,productId,variationId, and the post-type guard — so one kill would have spoken for one contract out of six. Each red fails at the assertion the matrix names and goes green on revert. The assertion counts corroborate the line numbers rather than just accompanying them: the clean run makes 20 assertions and the reds stop at 2, 2, 3, 6, 6 and 18, precisely where each mutated value is first read.ParseErrorand two greens that failed after revert, none of them real. The cause was the container's view of the bind-mounted source lagging the host's, so a run could execute a half-written or still-mutated file. Those runs were discarded and re-run behind a check that confirms the container hashes the same bytes as the host before either the red or the green is trusted.oxlintexits 0 on the three changed JS/TS files.lint:changes:branchexits 0.phpcsrun directly on the changed PHP test file reports two errors, and running phpcs on trunk's copy at the same path reports the identical pair — a missingstrict_typesdeclaration and a spacing error on a pre-existing closure that this change's insertion shifts from line 258 to line 344. Neither is introduced here.phpstan.neonscopes towoocommerce.php,src/andincludes/; no file in this diff is in scope.env:start:blockswith creating the storage state and snapshot that theblocks setupproject creates. A stale mutation summary that contradicted its own logs was also regenerated from them.Milestone
Changelog entry
Changelog Entry Details
Significance
Type
Message
Changelog Entry Comment
Comment
Created manually:
plugins/woocommerce/changelog/testops-234-products-blockandplugins/woocommerce/client/blocks/changelog/testops-234-products-block.Use of AI Tools
The migration was produced by an agent-run campaign with per-test mutation verification (see #68046). This PR was assembled, verified in isolation, and reviewed by an agent; the author reviewed the diff and takes responsibility for it.
🤖 Generated with Claude Code