PluginProbe ʕ •ᴥ•ʔ
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More / 2.10.0
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More v2.10.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 3 weeks ago Providers 3 weeks ago BulkRearmOnGrowthTest.php 3 weeks ago BulkReviewsUpdateStuckStateTest.php 3 weeks ago ClearCacheRelayResetTest.php 3 weeks ago DeleteSourceRelayFailureTest.php 3 weeks ago ErrorHandlerFalsyOptionTest.php 3 weeks ago FeedCacheUpdateServiceTest.php 3 weeks ago FeedMalformedPayloadTest.php 3 weeks ago ForceKeylessRefetchTest.php 3 weeks ago LicenseDeactivateStaleStateTest.php 3 weeks ago MediaFinderMemoTest.php 3 weeks ago MultiSourceAggregationTest.php 3 weeks ago ReconcileMigratedLicenseRoutineTest.php 3 weeks ago ReconcileRemovalTest.php 3 weeks ago RegisterWebsiteRoutineTest.php 3 weeks ago RemoteRequestMemoTest.php 3 weeks ago ReviewAlertHeaderTotalsTest.php 3 weeks ago ReviewAlertPageTargetingTest.php 3 weeks ago ReviewAlertStarFillTest.php 3 weeks ago ShortcodeNeutralizationTest.php 3 weeks ago SiteMigrationRecoveryTest.php 3 weeks ago Smash1583HeaderParityTest.php 3 weeks ago Smash1631MultiLanguageBulkTest.php 3 weeks ago Smash1631UpdateSingleLangScopeTest.php 3 weeks ago Smash1706TripAdvisorPlaceIdTest.php 3 weeks ago Smash1756SchemaServiceTest.php 3 weeks ago Smash1785AvatarLocalUrlGuardTest.php 3 weeks ago Smash1785AvatarReHealTest.php 3 weeks ago Smash1795ReviewTextXssTest.php 3 weeks ago Smash782BookingHeaderRatingTest.php 3 weeks ago Smash782CountryFlagEmojiTest.php 3 weeks ago Smash782ExternalRefreshCronTest.php 3 weeks ago Smash782ExtrasTemplateTest.php 3 weeks ago Smash782ReviewAlertProviderDataTest.php 3 weeks ago SourceIdLookupTest.php 3 weeks ago WpmlGetCurrentLanguageTest.php 3 weeks ago WpmlLanguageMappingTest.php 3 weeks ago
Smash782ExtrasTemplateTest.php
280 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 // THIS reviewer's own score, on Booking's 0-10 scale: the stored 0-5
142 // rating doubled, plus the band word for that score.
143 $out = $this->render('rating-extras/booking.php', [
144 'rating' => 4,
145 'provider' => ['name' => 'booking'],
146 ]);
147 $this->assertStringContainsString('class="sb-item-rating-score"', $out);
148 $this->assertStringContainsString('class="sb-item-rating-score-badge">8.0', $out);
149 $this->assertStringContainsString('class="sb-item-rating-score-label">Very good', $out);
150 }
151
152 public function test_booking_rating_extras_emits_nothing_when_rating_zero(): void
153 {
154 $out = $this->render('rating-extras/booking.php', [
155 'rating' => 0,
156 'provider' => ['name' => 'booking'],
157 ]);
158 $this->assertSame('', trim($out));
159 }
160
161 public function test_booking_score_and_word_derive_from_each_reviewers_own_rating(): void
162 {
163 // Score = this reviewer's 0-5 rating doubled; word = the band that score
164 // falls in (sbr_booking_score_word() carries each threshold's provenance).
165 $cases = [
166 ['rating' => 5, 'score' => '10.0', 'word' => 'Exceptional'],
167 ['rating' => 4.75, 'score' => '9.5', 'word' => 'Exceptional'],
168 ['rating' => 4.5, 'score' => '9.0', 'word' => 'Superb'],
169 ['rating' => 4, 'score' => '8.0', 'word' => 'Very good'],
170 ['rating' => 3.5, 'score' => '7.0', 'word' => 'Good'],
171 ['rating' => 3, 'score' => '6.0', 'word' => 'Pleasant'],
172 ];
173 foreach ($cases as $c) {
174 $out = $this->render('rating-extras/booking.php', ['rating' => $c['rating']]);
175 $this->assertStringContainsString('>' . $c['score'] . '<', $out, 'rating ' . $c['rating']);
176 $this->assertStringContainsString('>' . $c['word'] . '<', $out, 'rating ' . $c['rating']);
177 }
178
179 // Below the lowest band Booking publishes -> badge only, no invented word.
180 $out = $this->render('rating-extras/booking.php', ['rating' => 2.5]);
181 $this->assertStringContainsString('>5.0<', $out);
182 $this->assertStringNotContainsString('rating-score-label', $out);
183 }
184
185 public function test_booking_score_ignores_the_property_wide_metadata_score(): void
186 {
187 // Regression guard for the 6525848 fallout: metadata.review_score /
188 // review_score_word are the HOTEL's general rating and the relay stamps the
189 // same pair onto every card, so reading them here showed all 20 reviews as
190 // "9.5 Exceptional". The property score belongs in the feed header only.
191 $out = $this->render('rating-extras/booking.php', [
192 'rating' => 4,
193 'metadata' => ['review_score' => 9.5, 'review_score_word' => 'Exceptional'],
194 'provider' => ['name' => 'booking'],
195 ]);
196 $this->assertStringContainsString('>8.0<', $out);
197 $this->assertStringContainsString('>Very good<', $out);
198 $this->assertStringNotContainsString('9.5', $out);
199 $this->assertStringNotContainsString('Exceptional', $out);
200 }
201
202 public function test_booking_extras_renders_helpful_when_count_positive(): void
203 {
204 $out = $this->render('extras/booking.php', [
205 'metadata' => ['helpful_vote_count' => 14],
206 'provider' => ['name' => 'booking'],
207 ]);
208 // class list also carries the SMASH-782 section-spacing class
209 // (sbr-review-horizontal-element), so match the class prefix only.
210 $this->assertStringContainsString('class="sb-item-helpful', $out);
211 $this->assertStringContainsString('class="sb-item-helpful-icon"', $out);
212 $this->assertStringContainsString('14 people found this helpful', $out);
213 }
214
215 public function test_booking_extras_renders_singular_helpful(): void
216 {
217 $out = $this->render('extras/booking.php', [
218 'metadata' => ['helpful_vote_count' => 1],
219 'provider' => ['name' => 'booking'],
220 ]);
221 $this->assertStringContainsString('1 person found this helpful', $out);
222 }
223
224 public function test_booking_extras_emits_nothing_when_helpful_zero(): void
225 {
226 $out = $this->render('extras/booking.php', [
227 'metadata' => ['helpful_vote_count' => 0],
228 'provider' => ['name' => 'booking'],
229 ]);
230 $this->assertSame('', trim($out));
231 }
232
233 // -------------------------------------------------------------------------
234 // ALIEXPRESS extras + text-aliexpress
235 // -------------------------------------------------------------------------
236
237 public function test_aliexpress_extras_renders_variants_only_when_item_spec_present(): void
238 {
239 $out = $this->render('extras/aliexpress.php', [
240 'metadata' => ['item_spec' => 'Color:Black Size:XL'],
241 'provider' => ['name' => 'aliexpress'],
242 ]);
243 $this->assertStringContainsString('class="sb-item-variants', $out);
244 $this->assertStringContainsString('class="sb-item-variants-pill">Color: Black</span>', $out);
245 $this->assertStringContainsString('class="sb-item-variants-pill">Size: XL</span>', $out);
246 }
247
248 public function test_aliexpress_extras_emits_nothing_when_no_variants_no_followup(): void
249 {
250 $out = $this->render('extras/aliexpress.php', [
251 'metadata' => ['item_spec' => '', 'followup' => null],
252 'provider' => ['name' => 'aliexpress'],
253 ]);
254 $this->assertSame('', trim($out));
255 }
256
257 public function test_aliexpress_extras_renders_followup_block_when_metadata_populated(): void
258 {
259 $out = $this->render('extras/aliexpress.php', [
260 'metadata' => [
261 'item_spec' => '',
262 'followup' => [
263 'date' => '2 weeks later',
264 'text' => 'Still going strong.',
265 ],
266 ],
267 'provider' => ['name' => 'aliexpress'],
268 ]);
269
270 // class list also carries the SMASH-782 section-spacing class, so match prefix only.
271 $this->assertStringContainsString('class="sb-item-followup', $out);
272 $this->assertStringContainsString('class="sb-item-followup-header"', $out);
273 $this->assertStringContainsString('class="sb-item-followup-label">Follow-up Review', $out);
274 $this->assertStringContainsString('class="sb-item-followup-date">2 weeks later', $out);
275 $this->assertStringContainsString('class="sb-item-followup-text">Still going strong.', $out);
276 }
277 }
278
279 } // end namespace SbReviews\Tests\Unit
280