@@ -238,6 +238,23 @@ def fmt(v):
238238 return fmt (p .get ("price" , 0 )), fmt (p .get ("regular_price" , 0 ))
239239
240240
241+ REVIEWS_SEL = "#reviews, #review_form, .woocommerce-Reviews"
242+
243+
244+ def html_reviews_verdict (shown_before , shown_after ):
245+ """'ok' when reviews work on the HTML theme's product page, else the GAP.
246+
247+ shown_before: the reviews section renders for the product as it is (a
248+ design without reviews shows none until one exists — not a gap);
249+ shown_after: an approved test review rendered (None = not exercised, no
250+ wp-cli)."""
251+ if shown_after is True :
252+ return "ok"
253+ if shown_after is None :
254+ return "ok" if shown_before else "unverified"
255+ return "an approved review is not shown on its product page"
256+
257+
241258def enable_cod (run ):
242259 """Turn cash on delivery on for the audit's order; returns what to restore.
243260
@@ -488,13 +505,37 @@ def add_to_cart(p, row, expect_ok=True):
488505 gap ("my-account did not render a login form" , "my-account" )
489506
490507 anyrow = by ["simple" ] or by ["variable" ]
491- if anyrow :
508+ reviews_on = wpcli ("option get woocommerce_enable_reviews" ) if args .wp_cli else "yes"
509+ if anyrow and reviews_on != "no" :
510+ # A design without reviews shows none until a product HAS one, so
511+ # the check is that an approved review appears once it exists —
512+ # and that a product without one shows exactly the design.
492513 page .goto (product_url (anyrow ), wait_until = "networkidle" )
493514 page .wait_for_timeout (1200 )
494- if "review" in page .inner_text ("body" ).lower ():
495- ok ("a product page offers reviews" , "reviews" )
515+ before = bool (page .query_selector (REVIEWS_SEL ))
516+ after = None
517+ if args .wp_cli and anyrow .get ("id" ):
518+ cid = wpcli (f"comment create --comment_post_ID={ int (anyrow ['id' ])} --comment_type=review "
519+ "--comment_approved=1 --comment_author='Coverage Audit' "
520+ "--comment_author_email=audit@coverage.test "
521+ "--comment_content='Audit review: soft and warm.' --porcelain" )
522+ if cid :
523+ wpcli (f"comment meta add { int (cid )} rating 5" )
524+ wpcli (f"eval 'wc_delete_product_transients({ int (anyrow ['id' ])} ); "
525+ f"WC_Comments::clear_transients({ int (anyrow ['id' ])} );'" )
526+ page .goto (product_url (anyrow ), wait_until = "networkidle" )
527+ page .wait_for_timeout (1200 )
528+ box = page .query_selector (REVIEWS_SEL )
529+ after = bool (box ) and "Audit review: soft and warm." in box .inner_text ()
530+ wpcli (f"comment delete { int (cid )} --force" )
531+ wpcli (f"eval 'WC_Comments::clear_transients({ int (anyrow ['id' ])} );'" )
532+ verdict = html_reviews_verdict (before , after )
533+ if verdict == "ok" :
534+ ok ("an approved review shows on its product page" if after else "a product page offers reviews" , "reviews" )
535+ elif verdict == "unverified" :
536+ note ("reviews: none render yet (the design has none) — pass --wp-cli to prove an approved one shows" )
496537 else :
497- gap ("reviews are enabled in Woo but rendered nowhere" , "reviews" )
538+ gap (verdict , "reviews" )
498539
499540 page .goto (f"{ BASE } /cart/" , wait_until = "networkidle" )
500541 page .wait_for_timeout (2500 )
@@ -610,6 +651,21 @@ def spec_frozen(specs, rows):
610651 return not all (squash (specs [0 ]) in own_text (r ) for r in rows )
611652
612653
654+ def reviews_verdict (in_template , shown_before , shown_after ):
655+ """'ok' when reviews work on a product page, else the GAP text.
656+
657+ in_template: the product template places a reviews block (None = unknown,
658+ no wp-cli); shown_before: reviews render with the product as it is;
659+ shown_after: an approved test review rendered (None = not exercised)."""
660+ if in_template is False :
661+ return "reviews are enabled in Woo but the product template has no reviews block"
662+ if shown_after is True or (shown_after is None and shown_before ):
663+ return "ok"
664+ if shown_after is None :
665+ return "reviews are enabled in Woo but rendered nowhere"
666+ return "an approved review is not shown on its product page"
667+
668+
613669def soldout_ids (row ):
614670 """The product and every variation of it — what a basket line can carry."""
615671 return {row .get ("id" )} | {v .get ("id" ) for v in row .get ("variations" ) or [] if isinstance (v , dict )}
@@ -1098,16 +1154,36 @@ def click_add():
10981154 else :
10991155 gap ("no published page renders the WooCommerce account form" , "my-account" )
11001156
1157+ # A block theme may show its reviews block only once a product has a
1158+ # review (a design without one stays as drawn), so "reviews work" is:
1159+ # enabled in WooCommerce, placed in the product template, and a real
1160+ # approved review rendered on the page (then deleted again).
11011161 reviews_on = wpcli ("option get woocommerce_enable_reviews" ) if args .wp_cli else "yes"
11021162 if anyrow and reviews_on != "no" :
1163+ REVIEWS = ("#reviews, .woocommerce-Reviews, .wp-block-woocommerce-product-reviews, "
1164+ ".wp-block-woocommerce-product-details" )
1165+ in_template = None
1166+ if args .wp_cli :
1167+ found = wpcli ("eval \" $t = get_block_template(get_stylesheet() . '//single-product'); echo $t ? (int) (strpos($t->content, 'wp:woocommerce/product-reviews') !== false || strpos($t->content, 'wp:woocommerce/product-details') !== false) : 2;\" " )
1168+ in_template = found == "1" if found in ("0" , "1" ) else None
11031169 go (anyrow ["permalink" ])
1104- if page .query_selector ("#reviews, #review_form, .woocommerce-Reviews, "
1105- ".wp-block-woocommerce-product-reviews, "
1106- ".wp-block-woocommerce-product-review-form, "
1107- ".wp-block-woocommerce-product-details" ):
1108- ok ("a product page offers reviews" , "reviews" )
1170+ before = page .query_selector (REVIEWS ) is not None
1171+ after , review = None , None
1172+ if args .wp_cli :
1173+ marker = "Woo audit review " + str (anyrow ["id" ])
1174+ cid = wpcli (f"comment create --comment_post_ID={ int (anyrow ['id' ])} --comment_type=review --comment_approved=1 "
1175+ f"--comment_author=Audit --comment_author_email=audit@example.com --comment_content={ shlex .quote (marker )} --porcelain" )
1176+ if cid and cid .isdigit ():
1177+ cleanup .append (f"comment delete { cid } --force" )
1178+ go (anyrow ["permalink" ])
1179+ block = page .query_selector (REVIEWS )
1180+ after = bool (block and marker in (block .inner_text () or "" ))
1181+ wpcli (f"comment delete { cid } --force" )
1182+ verdict = reviews_verdict (in_template , before , after )
1183+ if verdict == "ok" :
1184+ ok ("a product page offers reviews" + (" (rendered once a review exists)" if after and not before else "" ), "reviews" )
11091185 else :
1110- gap ("reviews are enabled in Woo but rendered nowhere" , "reviews" )
1186+ gap (verdict , "reviews" )
11111187
11121188 if cart_url :
11131189 go (cart_url , 2500 )
0 commit comments