PluginProbe ʕ •ᴥ•ʔ
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More / 2.11.0
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More v2.11.0
2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.7 2.6.8 2.6.5 2.6.4 2.6.3 2.6.2 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 trunk 1.0 1.0.1 1.0.2 1.0.3 1.1 1.1.1 1.1.2 1.2.0 2.0 2.1.0 2.1.1 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1
reviews-feed / tests / Unit / Providers / EddTitleRenderingBcTest.php
reviews-feed / tests / Unit / Providers Last commit date
DuplicatePreventionTest.php 1 week ago EddItemTemplateBcTest.php 1 week ago EddProviderGateTest.php 1 week ago EddTitleRenderingBcTest.php 1 week ago Smash782AirbnbUrlListingIdTest.php 1 week ago Smash782BookingUrlHotelIdTest.php 1 week ago
EddTitleRenderingBcTest.php
167 lines
1 <?php
2
3 declare(strict_types=1);
4
5 namespace SmashBalloon\Reviews\Tests\Unit\Providers;
6
7 use PHPUnit\Framework\TestCase;
8
9 /**
10 * SMASH-1553 — BC pin for the templates/frontend/post-elements/text.php
11 * provider-title gate.
12 *
13 * The PR adds a review-title render block to the default text template,
14 * gated by a provider allowlist. The gate exists because
15 * Util::parse_single_review() at class/Common/Util.php:1421 synthesizes
16 * a substring fallback title from the review text whenever the upstream
17 * provider omits one:
18 *
19 * 'title' => isset($review['title']) ? $review['title'] : substr($review['text'], 0, 40)
20 *
21 * That fallback fires for every review stored via Collection/Import paths
22 * (PostAggregator::insert_multiple_reviews at PostAggregator.php:458 and
23 * SBR_Feed_Saver_Manager::import_reviews_collection at the equivalent
24 * lines). Result: Google / Yelp / Facebook / TripAdvisor / Trustpilot /
25 * WP.org / WooCommerce / Airbnb / AliExpress reviews stored via Collection
26 * carry a synthesized "first 40 chars of review text" value in $post['title'].
27 *
28 * A naked `!empty($post['title'])` guard in the template would surface that
29 * synthesized substring as a bold title above the full review text — a
30 * visible BC regression for every existing Collection feed.
31 *
32 * These tests reproduce the gate logic from text.php exactly so future
33 * edits to the template must touch this helper in lockstep — intentionally
34 * tight coupling.
35 */
36 class EddTitleRenderingBcTest extends TestCase {
37
38 /**
39 * Reproduces the gate logic from templates/frontend/post-elements/text.php
40 * (the `$providers_with_review_titles` allowlist block introduced in
41 * SMASH-1553). If text.php changes, this helper must change in lockstep.
42 *
43 * @param array<string,mixed> $post Shape used by the default text template.
44 * @return bool Whether the title block should render.
45 */
46 private static function shouldRenderTitle( array $post ): bool {
47 $providers_with_review_titles = array( 'edd' );
48 $post_provider = ! empty( $post['provider']['name'] ) ? $post['provider']['name'] : '';
49 return in_array( $post_provider, $providers_with_review_titles, true )
50 && ! empty( $post['title'] )
51 && is_string( $post['title'] );
52 }
53
54 public function test_edd_review_with_real_title_renders(): void {
55 // EDD's submission form makes the title field required, so every
56 // real EDD review carries an authoritative customer-typed title.
57 $post = array(
58 'provider' => array( 'name' => 'edd' ),
59 'title' => 'Excellent plugin — easy to set up',
60 'text' => 'I bought this last week and it just works.',
61 );
62 $this->assertTrue(
63 self::shouldRenderTitle( $post ),
64 'EDD reviews with a real title MUST render the title block.'
65 );
66 }
67
68 public function test_google_review_with_synthesized_title_does_not_render(): void {
69 // Shape produced by Util::parse_single_review() at Util.php:1421 when
70 // a Google review is stored via the Collection / Import path: the
71 // `title` field is synthesized as the first 40 chars of `text`.
72 // Pre-SMASH-1553, the default template never read `title`, so this
73 // fake value was harmless. The new title block MUST exclude this
74 // case via the provider allowlist.
75 $post = array(
76 'provider' => array( 'name' => 'google' ),
77 'title' => 'Great service! Will definitely come ba',
78 'text' => 'Great service! Will definitely come back next time I am in town.',
79 );
80 $this->assertFalse(
81 self::shouldRenderTitle( $post ),
82 'BC: Google reviews carrying a synthesized substring title (from Util.php:1421) MUST NOT render the title block — that would surface fake duplicated text above the review body.'
83 );
84 }
85
86 public function test_yelp_review_with_synthesized_title_does_not_render(): void {
87 $post = array(
88 'provider' => array( 'name' => 'yelp' ),
89 'title' => 'Service was a bit slow but the food m',
90 'text' => 'Service was a bit slow but the food more than made up for it.',
91 );
92 $this->assertFalse( self::shouldRenderTitle( $post ), 'BC: Yelp Collection reviews must not render the synthesized title.' );
93 }
94
95 public function test_facebook_review_with_synthesized_title_does_not_render(): void {
96 $post = array(
97 'provider' => array( 'name' => 'facebook' ),
98 'title' => 'Highly recommend this place — friendly',
99 'text' => 'Highly recommend this place — friendly staff and great prices.',
100 );
101 $this->assertFalse( self::shouldRenderTitle( $post ), 'BC: Facebook Collection reviews must not render the synthesized title.' );
102 }
103
104 public function test_woocommerce_review_with_empty_title_does_not_render(): void {
105 // WooCommerce direct-ingest sets `'title' => ''` (WooCommerce.php:298,427).
106 // Even if WooCommerce were on the allowlist, the empty-string check
107 // should prevent rendering.
108 $post = array(
109 'provider' => array( 'name' => 'woocommerce' ),
110 'title' => '',
111 'text' => 'Decent product.',
112 );
113 $this->assertFalse( self::shouldRenderTitle( $post ), 'WooCommerce empty-string title must not render.' );
114 }
115
116 public function test_edd_review_with_missing_title_does_not_render(): void {
117 // Defensive: EDD reviews predating the title field, or rows where
118 // the meta was somehow lost. Empty/missing title must skip the block.
119 $post = array(
120 'provider' => array( 'name' => 'edd' ),
121 'text' => 'No-title legacy EDD review.',
122 );
123 $this->assertFalse( self::shouldRenderTitle( $post ), 'EDD reviews without a title field must not render an empty heading.' );
124
125 $post['title'] = null;
126 $this->assertFalse( self::shouldRenderTitle( $post ), 'EDD reviews with null title must not render.' );
127
128 $post['title'] = '';
129 $this->assertFalse( self::shouldRenderTitle( $post ), 'EDD reviews with empty-string title must not render.' );
130 }
131
132 public function test_edd_review_with_non_string_title_does_not_render(): void {
133 // Defense in depth: if any future ingest path stuffs a non-string
134 // value into `title` (legacy data, dev fixture, schema drift), the
135 // `is_string()` guard prevents the template's `esc_html()` call from
136 // fatalling on PHP 8+ (which requires a string argument).
137 $post = array(
138 'provider' => array( 'name' => 'edd' ),
139 'title' => array( 'nested', 'shape' ),
140 'text' => 'Review text.',
141 );
142 $this->assertFalse( self::shouldRenderTitle( $post ), 'Array title must not render — would fatal on esc_html().' );
143
144 $post['title'] = (object) array( 'value' => 'x' );
145 $this->assertFalse( self::shouldRenderTitle( $post ), 'Object title must not render.' );
146
147 $post['title'] = 12345;
148 $this->assertFalse( self::shouldRenderTitle( $post ), 'Integer title must not render — defends against silent string-cast surprises.' );
149 }
150
151 public function test_missing_provider_name_does_not_render(): void {
152 // Defensive: malformed $post with no provider name (unusual, but
153 // possible in legacy data or partial migrations).
154 $post = array(
155 'title' => 'EDD review title',
156 'text' => 'Some text.',
157 );
158 $this->assertFalse( self::shouldRenderTitle( $post ), 'Missing provider.name must not render the title block.' );
159
160 $post['provider'] = array();
161 $this->assertFalse( self::shouldRenderTitle( $post ), 'Empty provider array must not render.' );
162
163 $post['provider'] = array( 'name' => '' );
164 $this->assertFalse( self::shouldRenderTitle( $post ), 'Empty provider name must not render.' );
165 }
166 }
167