PluginProbe ʕ •ᴥ•ʔ
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More / 2.7.0
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More v2.7.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 / Smash782ExtrasTemplateTest.php
reviews-feed / tests / Unit Last commit date
Doubles 1 month ago Providers 1 month ago BulkRearmOnGrowthTest.php 1 month ago BulkReviewsUpdateStuckStateTest.php 1 month ago ClearCacheRelayResetTest.php 1 month ago DeleteSourceRelayFailureTest.php 1 month ago ErrorHandlerFalsyOptionTest.php 1 month ago FeedCacheUpdateServiceTest.php 1 month ago FeedMalformedPayloadTest.php 1 month ago ForceKeylessRefetchTest.php 1 month ago LicenseDeactivateStaleStateTest.php 1 month ago MediaFinderMemoTest.php 1 month ago MultiSourceAggregationTest.php 1 month ago ReconcileMigratedLicenseRoutineTest.php 1 month ago ReconcileRemovalTest.php 1 month ago RegisterWebsiteRoutineTest.php 1 month ago RemoteRequestMemoTest.php 1 month ago ReviewAlertHeaderTotalsTest.php 1 month ago ReviewAlertPageTargetingTest.php 1 month ago ReviewAlertStarFillTest.php 1 month ago ShortcodeNeutralizationTest.php 1 month ago SiteMigrationRecoveryTest.php 1 month ago Smash1583HeaderParityTest.php 1 month ago Smash1631MultiLanguageBulkTest.php 1 month ago Smash1631UpdateSingleLangScopeTest.php 1 month ago Smash782BookingHeaderRatingTest.php 1 month ago Smash782CountryFlagEmojiTest.php 1 month ago Smash782ExternalRefreshCronTest.php 1 month ago Smash782ExtrasTemplateTest.php 1 month ago Smash782ReviewAlertProviderDataTest.php 1 month ago SourceIdLookupTest.php 1 month ago WpmlGetCurrentLanguageTest.php 1 month ago WpmlLanguageMappingTest.php 1 month ago
Smash782ExtrasTemplateTest.php
267 lines
1 <?php
2
3 // Stub WP escape + i18n helpers in the GLOBAL namespace so plugin templates
4 // (which call `esc_html`, `esc_attr`, etc. as global functions) can resolve
5 // them when included from these unit tests. The plugin's tests/bootstrap.php
6 // already stubs sanitize_text_field / __() / etc., but not these.
7 namespace {
8 if (!function_exists('esc_html')) {
9 function esc_html($s)
10 {
11 return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
12 }
13 }
14 if (!function_exists('esc_attr')) {
15 function esc_attr($s)
16 {
17 return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
18 }
19 }
20 if (!function_exists('esc_url')) {
21 function esc_url($url)
22 {
23 return (string) $url;
24 }
25 }
26 if (!function_exists('esc_html__')) {
27 function esc_html__($text, $domain = null)
28 {
29 return esc_html($text);
30 }
31 }
32 if (!function_exists('_n')) {
33 function _n($single, $plural, $number, $domain = null)
34 {
35 return (int) $number === 1 ? $single : $plural;
36 }
37 }
38 if (!function_exists('wp_kses_post')) {
39 function wp_kses_post($html)
40 {
41 return $html;
42 }
43 }
44 }
45
46 namespace SbReviews\Tests\Unit {
47
48 use PHPUnit\Framework\TestCase;
49
50 /**
51 * SMASH-782 Phase 2 — plugin template render tests.
52 *
53 * Each of the new per-provider templates (extras + rating-extras +
54 * text-aliexpress) is pure PHP that takes `$post` and outputs HTML.
55 * These tests render the templates with fixtures mirroring real relay
56 * output and assert the HTML contains the prototype-required classes
57 * AND that templates emit NOTHING when real data is missing
58 * (no demo / static fallback content).
59 */
60 class Smash782ExtrasTemplateTest extends TestCase
61 {
62 private const TEMPLATES_DIR = __DIR__ . '/../../templates/frontend/post-elements';
63
64 /**
65 * Render a template with `$post` in scope and capture output.
66 *
67 * PHPUnit 10 with `beStrictAboutOutputDuringTests="true"` tracks output
68 * buffer count and fails if buffers we open aren't closed. The level guard
69 * below force-closes any buffer left open by the template (even if it
70 * returned early through an exception path), so PHPUnit's count matches.
71 */
72 private function render(string $relPath, array $post): string
73 {
74 $startLevel = ob_get_level();
75 ob_start();
76 try {
77 include self::TEMPLATES_DIR . '/' . $relPath;
78 } finally {
79 $out = '';
80 while (ob_get_level() > $startLevel) {
81 $out = ob_get_clean() . $out;
82 }
83 }
84 return $out;
85 }
86
87 // -------------------------------------------------------------------------
88 // AIRBNB extras
89 // -------------------------------------------------------------------------
90
91 public function test_airbnb_reply_renders_full_block_when_response_present(): void
92 {
93 $out = $this->render('extras/airbnb.php', [
94 'response' => 'Thanks for staying!',
95 'reply' => [
96 'name' => 'Bill',
97 'avatar' => 'https://muscache/host.jpg',
98 ],
99 'provider' => ['name' => 'airbnb'],
100 ]);
101
102 $this->assertStringContainsString('class="sb-item-reply"', $out);
103 $this->assertStringContainsString('data-from="host"', $out);
104 $this->assertStringContainsString('class="sb-item-reply-header"', $out);
105 $this->assertStringContainsString('<img class="sb-item-reply-avatar"', $out);
106 $this->assertStringContainsString('src="https://muscache/host.jpg"', $out);
107 $this->assertStringContainsString('class="sb-item-reply-name">Bill', $out);
108 $this->assertStringContainsString('class="sb-item-reply-label">Host', $out);
109 $this->assertStringContainsString('class="sb-item-reply-text">Thanks for staying!', $out);
110 }
111
112 public function test_airbnb_reply_emits_nothing_when_response_empty(): void
113 {
114 $out = $this->render('extras/airbnb.php', [
115 'response' => '',
116 'reply' => ['name' => 'Bill', 'avatar' => 'https://x.jpg'],
117 'provider' => ['name' => 'airbnb'],
118 ]);
119 $this->assertSame('', trim($out), 'No demo fallback — empty response must produce empty output.');
120 }
121
122 public function test_airbnb_reply_renders_without_avatar_when_only_text(): void
123 {
124 $out = $this->render('extras/airbnb.php', [
125 'response' => 'Thanks!',
126 'reply' => ['name' => '', 'avatar' => ''],
127 'provider' => ['name' => 'airbnb'],
128 ]);
129 $this->assertStringContainsString('class="sb-item-reply"', $out);
130 $this->assertStringContainsString('class="sb-item-reply-text">Thanks!', $out);
131 $this->assertStringNotContainsString('sb-item-reply-avatar', $out);
132 $this->assertStringNotContainsString('sb-item-reply-name', $out);
133 }
134
135 // -------------------------------------------------------------------------
136 // BOOKING extras + rating-extras
137 // -------------------------------------------------------------------------
138
139 public function test_booking_rating_extras_renders_score_badge_inside_rating_slot(): void
140 {
141 // Booking's REAL 0-10 hotel score + word, forwarded by the relay as
142 // metadata.review_score / metadata.review_score_word (no derived value,
143 // no invented bands — see rating-extras/booking.php docblock).
144 $out = $this->render('rating-extras/booking.php', [
145 'metadata' => ['review_score' => 8.7, 'review_score_word' => 'Very Good'],
146 'provider' => ['name' => 'booking'],
147 ]);
148 $this->assertStringContainsString('class="sb-item-rating-score"', $out);
149 $this->assertStringContainsString('class="sb-item-rating-score-badge">8.7', $out);
150 $this->assertStringContainsString('class="sb-item-rating-score-label">Very Good', $out);
151 }
152
153 public function test_booking_rating_extras_emits_nothing_when_rating_zero(): void
154 {
155 $out = $this->render('rating-extras/booking.php', [
156 'rating' => 0,
157 'provider' => ['name' => 'booking'],
158 ]);
159 $this->assertSame('', trim($out));
160 }
161
162 public function test_booking_score_renders_real_metadata_score_and_word_verbatim(): void
163 {
164 // The badge shows Booking's real 0-10 score verbatim (number_format 1dp)
165 // and the label shows Booking's own word verbatim — never a value/band
166 // derived from the 0-5 star rating.
167 $cases = [
168 ['score' => 10.0, 'word' => 'Exceptional'],
169 ['score' => 8.5, 'word' => 'Very Good'],
170 ['score' => 7.5, 'word' => 'Good'],
171 ['score' => 6.5, 'word' => 'Pleasant'],
172 ];
173 foreach ($cases as $c) {
174 $out = $this->render('rating-extras/booking.php', [
175 'metadata' => ['review_score' => $c['score'], 'review_score_word' => $c['word']],
176 ]);
177 $this->assertStringContainsString('>' . number_format($c['score'], 1) . '<', $out, 'score ' . $c['score']);
178 $this->assertStringContainsString($c['word'], $out, 'score ' . $c['score']);
179 }
180
181 // Real score present but no word from the relay -> badge only, no label.
182 $out = $this->render('rating-extras/booking.php', [
183 'metadata' => ['review_score' => 8.0],
184 ]);
185 $this->assertStringContainsString('>8.0<', $out);
186 $this->assertStringNotContainsString('rating-score-label', $out);
187 }
188
189 public function test_booking_extras_renders_helpful_when_count_positive(): void
190 {
191 $out = $this->render('extras/booking.php', [
192 'metadata' => ['helpful_vote_count' => 14],
193 'provider' => ['name' => 'booking'],
194 ]);
195 // class list also carries the SMASH-782 section-spacing class
196 // (sbr-review-horizontal-element), so match the class prefix only.
197 $this->assertStringContainsString('class="sb-item-helpful', $out);
198 $this->assertStringContainsString('class="sb-item-helpful-icon"', $out);
199 $this->assertStringContainsString('14 people found this helpful', $out);
200 }
201
202 public function test_booking_extras_renders_singular_helpful(): void
203 {
204 $out = $this->render('extras/booking.php', [
205 'metadata' => ['helpful_vote_count' => 1],
206 'provider' => ['name' => 'booking'],
207 ]);
208 $this->assertStringContainsString('1 person found this helpful', $out);
209 }
210
211 public function test_booking_extras_emits_nothing_when_helpful_zero(): void
212 {
213 $out = $this->render('extras/booking.php', [
214 'metadata' => ['helpful_vote_count' => 0],
215 'provider' => ['name' => 'booking'],
216 ]);
217 $this->assertSame('', trim($out));
218 }
219
220 // -------------------------------------------------------------------------
221 // ALIEXPRESS extras + text-aliexpress
222 // -------------------------------------------------------------------------
223
224 public function test_aliexpress_extras_renders_variants_only_when_item_spec_present(): void
225 {
226 $out = $this->render('extras/aliexpress.php', [
227 'metadata' => ['item_spec' => 'Color:Black Size:XL'],
228 'provider' => ['name' => 'aliexpress'],
229 ]);
230 $this->assertStringContainsString('class="sb-item-variants', $out);
231 $this->assertStringContainsString('class="sb-item-variants-pill">Color: Black</span>', $out);
232 $this->assertStringContainsString('class="sb-item-variants-pill">Size: XL</span>', $out);
233 }
234
235 public function test_aliexpress_extras_emits_nothing_when_no_variants_no_followup(): void
236 {
237 $out = $this->render('extras/aliexpress.php', [
238 'metadata' => ['item_spec' => '', 'followup' => null],
239 'provider' => ['name' => 'aliexpress'],
240 ]);
241 $this->assertSame('', trim($out));
242 }
243
244 public function test_aliexpress_extras_renders_followup_block_when_metadata_populated(): void
245 {
246 $out = $this->render('extras/aliexpress.php', [
247 'metadata' => [
248 'item_spec' => '',
249 'followup' => [
250 'date' => '2 weeks later',
251 'text' => 'Still going strong.',
252 ],
253 ],
254 'provider' => ['name' => 'aliexpress'],
255 ]);
256
257 // class list also carries the SMASH-782 section-spacing class, so match prefix only.
258 $this->assertStringContainsString('class="sb-item-followup', $out);
259 $this->assertStringContainsString('class="sb-item-followup-header"', $out);
260 $this->assertStringContainsString('class="sb-item-followup-label">Follow-up Review', $out);
261 $this->assertStringContainsString('class="sb-item-followup-date">2 weeks later', $out);
262 $this->assertStringContainsString('class="sb-item-followup-text">Still going strong.', $out);
263 }
264 }
265
266 } // end namespace SbReviews\Tests\Unit
267