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 / bootstrap.php
reviews-feed / tests Last commit date
Unit 1 month ago bootstrap.php 1 month ago phpunit.xml 1 month ago
bootstrap.php
412 lines
1 <?php
2
3 /**
4 * PHPUnit bootstrap file for sb-reviews plugin tests.
5 *
6 * These tests are designed to run without the full WordPress environment
7 * by mocking WordPress functions and focusing on unit-testable logic.
8 */
9
10 // Define WordPress stubs for functions used in tested code
11 if (!defined('ABSPATH')) {
12 define('ABSPATH', dirname(__DIR__) . '/');
13 }
14
15 if (!defined('SBR_RELAY_BASE_URL')) {
16 define('SBR_RELAY_BASE_URL', 'https://relay.smashballoon.com/api/v1.0/');
17 }
18
19 // Plugin constants required when tests `require` the full sbr-functions.php.
20 if (!defined('SBR_PLUGIN_BASENAME')) {
21 define('SBR_PLUGIN_BASENAME', 'reviews-feed-pro/sb-reviews-pro.php');
22 }
23 if (!defined('SBR_PLUGIN_URL')) {
24 define('SBR_PLUGIN_URL', 'https://example.test/wp-content/plugins/reviews-feed-pro/');
25 }
26 if (!defined('SBRVER')) {
27 define('SBRVER', '2.5.0-test');
28 }
29 // Pro-side constants — required for silent-reactivation Pro-path tests.
30 // Pro tests will have these defined; the Free-skip path is covered by the
31 // license_key==='' early return, since Free installs never populate a key.
32 if (!defined('SBR_PLUGIN_NAME')) {
33 define('SBR_PLUGIN_NAME', 'Reviews Feed Pro Test');
34 }
35 if (!defined('SBR_PRODUCT_ID')) {
36 define('SBR_PRODUCT_ID', 9999999);
37 }
38 // Feeds table name — mirrors the runtime define (plugin bootstrap.php) so tests
39 // exercising queries that use SBR_FEEDS_TABLE (e.g. feed_localizations_for_source)
40 // resolve the constant instead of erroring on an undefined constant.
41 if (!defined('SBR_FEEDS_TABLE')) {
42 define('SBR_FEEDS_TABLE', 'sbr_feeds');
43 }
44 // Reviews-posts table name — mirrors the runtime define (plugin bootstrap.php) so
45 // tests that load SinglePostCache (POSTS_TABLE_NAME = SBR_POSTS_TABLE class const)
46 // resolve the constant instead of erroring on class load.
47 if (!defined('SBR_POSTS_TABLE')) {
48 define('SBR_POSTS_TABLE', 'sbr_reviews_posts');
49 }
50
51 // Sources table name — mirrors the runtime define so tests exercising the
52 // external-provider refresh path (SBR_Sources::sources_by_providers) resolve
53 // the constant instead of erroring on an undefined constant.
54 if (!defined('SBR_SOURCES_TABLE')) {
55 define('SBR_SOURCES_TABLE', 'sbr_sources');
56 }
57
58 // Mock WordPress functions used in tested code
59 if (!function_exists('sanitize_text_field')) {
60 function sanitize_text_field($str)
61 {
62 return trim(strip_tags($str));
63 }
64 }
65
66 if (!function_exists('absint')) {
67 function absint($maybeint)
68 {
69 return abs((int) $maybeint);
70 }
71 }
72
73 if (!function_exists('get_option')) {
74 function get_option($option, $default = false)
75 {
76 global $wp_options_mock;
77 return $wp_options_mock[$option] ?? $default;
78 }
79 }
80
81 if (!function_exists('wp_parse_url')) {
82 function wp_parse_url($url, $component = -1)
83 {
84 return parse_url($url, $component);
85 }
86 }
87
88 if (!function_exists('wp_parse_args')) {
89 function wp_parse_args($args, $defaults = [])
90 {
91 if (is_object($args)) {
92 $args = get_object_vars($args);
93 } elseif (!is_array($args)) {
94 parse_str((string) $args, $args);
95 }
96 return array_merge($defaults, $args);
97 }
98 }
99
100 if (!function_exists('update_option')) {
101 // Core signature: update_option($option, $value, $autoload = null).
102 // $autoload is accepted for signature parity so other tests exercising code
103 // that passes it don't fail with "Too many arguments".
104 function update_option($option, $value, $autoload = null)
105 {
106 global $wp_options_mock;
107 if (!is_array($wp_options_mock)) {
108 $wp_options_mock = [];
109 }
110 $wp_options_mock[$option] = $value;
111 return true;
112 }
113 }
114
115 if (!function_exists('delete_option')) {
116 function delete_option($option)
117 {
118 global $wp_options_mock;
119 if (!is_array($wp_options_mock)) {
120 $wp_options_mock = [];
121 return true;
122 }
123 unset($wp_options_mock[$option]);
124 return true;
125 }
126 }
127
128 if (!function_exists('get_home_url')) {
129 // Core signature: get_home_url($blog_id = null, $path = '', $scheme = null).
130 // Accept and ignore the extra args so callers using the full form don't error.
131 function get_home_url($blog_id = null, $path = '', $scheme = null)
132 {
133 global $wp_home_url_mock;
134 return $wp_home_url_mock ?? '';
135 }
136 }
137
138 if (!function_exists('wp_json_encode')) {
139 function wp_json_encode($data, $options = 0, $depth = 512)
140 {
141 return json_encode($data, $options, $depth);
142 }
143 }
144
145 if (!function_exists('esc_sql')) {
146 function esc_sql($data)
147 {
148 return is_array($data) ? array_map('esc_sql', $data) : addslashes((string) $data);
149 }
150 }
151
152 if (!function_exists('trailingslashit')) {
153 function trailingslashit($string)
154 {
155 return rtrim($string, '/\\') . '/';
156 }
157 }
158
159 if (!function_exists('wp_upload_dir')) {
160 function wp_upload_dir($time = null, $create_dir = true, $refresh_cache = false)
161 {
162 return [
163 'path' => '/tmp/uploads',
164 'url' => 'https://example.test/wp-content/uploads',
165 'subdir' => '',
166 'basedir' => '/tmp/uploads',
167 'baseurl' => 'https://example.test/wp-content/uploads',
168 'error' => false,
169 ];
170 }
171 }
172
173 // Stubs that let tests `require_once 'class/sbr-functions.php'` without
174 // triggering WordPress-only bootstrap calls at the top level.
175 if (!function_exists('register_activation_hook')) {
176 function register_activation_hook($file, $callback)
177 {
178 // no-op for tests
179 }
180 }
181 if (!function_exists('add_action')) {
182 function add_action($hook, $callback, $priority = 10, $accepted_args = 1)
183 {
184 // no-op for tests
185 }
186 }
187 if (!function_exists('add_filter')) {
188 function add_filter($hook, $callback, $priority = 10, $accepted_args = 1)
189 {
190 // no-op for tests
191 }
192 }
193 if (!function_exists('apply_filters')) {
194 function apply_filters($hook, $value, ...$args)
195 {
196 // Tests can inject a return value per hook via $wp_filter_mock (e.g. to
197 // simulate WPML's wpml_active_languages / wpml_current_language). With no
198 // mock set the stub keeps its original passthrough behavior.
199 global $wp_filter_mock;
200 if (isset($wp_filter_mock[$hook])) {
201 return $wp_filter_mock[$hook];
202 }
203 return $value;
204 }
205 }
206 if (!function_exists('do_action')) {
207 function do_action($hook, ...$args)
208 {
209 // no-op for tests
210 }
211 }
212
213 // Transient stubs for silent-reactivation rate-limit + notice-payload tests.
214 // Stored in a dedicated global ($wp_transients_mock) so tests can manipulate
215 // them independently from $wp_options_mock.
216 if (!defined('HOUR_IN_SECONDS')) {
217 define('HOUR_IN_SECONDS', 3600);
218 }
219 if (!defined('DAY_IN_SECONDS')) {
220 define('DAY_IN_SECONDS', 86400);
221 }
222 if (!defined('WEEK_IN_SECONDS')) {
223 define('WEEK_IN_SECONDS', 604800);
224 }
225 if (!defined('HOUR_IN_SECONDS')) {
226 define('HOUR_IN_SECONDS', 3600);
227 }
228 if (!defined('MINUTE_IN_SECONDS')) {
229 define('MINUTE_IN_SECONDS', 60);
230 }
231 // `wpdb::get_results()` output_type constants — production code passes ARRAY_A.
232 if (!defined('ARRAY_A')) {
233 define('ARRAY_A', 'ARRAY_A');
234 }
235 if (!defined('ARRAY_N')) {
236 define('ARRAY_N', 'ARRAY_N');
237 }
238 if (!defined('OBJECT')) {
239 define('OBJECT', 'OBJECT');
240 }
241 if (!function_exists('get_transient')) {
242 function get_transient($key)
243 {
244 global $wp_transients_mock;
245 if (!is_array($wp_transients_mock) || !isset($wp_transients_mock[$key])) {
246 return false;
247 }
248 return $wp_transients_mock[$key];
249 }
250 }
251 if (!function_exists('set_transient')) {
252 function set_transient($key, $value, $ttl = 0)
253 {
254 global $wp_transients_mock;
255 if (!is_array($wp_transients_mock)) {
256 $wp_transients_mock = [];
257 }
258 $wp_transients_mock[$key] = $value;
259 return true;
260 }
261 }
262 if (!function_exists('delete_transient')) {
263 function delete_transient($key)
264 {
265 global $wp_transients_mock;
266 if (!is_array($wp_transients_mock)) {
267 return true;
268 }
269 unset($wp_transients_mock[$key]);
270 return true;
271 }
272 }
273
274 // Minimal $wpdb double so DB-touching helpers (e.g. clear_plugin_cache) no-op
275 // instead of fatalling in unit tests. Guarded so any test that installs its own
276 // $wpdb wins.
277 if (!isset($GLOBALS['wpdb'])) {
278 $GLOBALS['wpdb'] = new class {
279 public $prefix = 'wp_';
280 public function query($sql)
281 {
282 return 0;
283 }
284 public function get_results($sql, $output = null)
285 {
286 return [];
287 }
288 public function get_var($sql)
289 {
290 return null;
291 }
292 public function get_row($sql, $output = null)
293 {
294 return null;
295 }
296 public function prepare($query, ...$args)
297 {
298 return $query;
299 }
300 public function esc_like($text)
301 {
302 return addcslashes((string) $text, '_%\\');
303 }
304 };
305 }
306
307 // Stub is_plugin_active for provider-detection tests (EDD provider gate).
308 // Backed by $wp_active_plugins_mock so tests can flip plugin-presence per case
309 // without touching real wp-admin includes.
310 if (!function_exists('is_plugin_active')) {
311 function is_plugin_active($plugin_path)
312 {
313 global $wp_active_plugins_mock;
314 if (!is_array($wp_active_plugins_mock)) {
315 return false;
316 }
317 return in_array($plugin_path, $wp_active_plugins_mock, true);
318 }
319 }
320
321 // i18n stub used by translatable strings in tested code paths.
322 if (!function_exists('__')) {
323 function __($text, $domain = null)
324 {
325 return $text;
326 }
327 }
328
329 // WP HTTP helpers — never actually invoked in unit tests (SBRelay::call is
330 // mocked at the `onlyMethods(['call'])` level), but reverify_token_via_register
331 // has a `function_exists` defense-in-depth guard that bails early if these
332 // helpers aren't defined. Without these stubs the guard fires in tests and
333 // reverify never reaches the mocked `call()`.
334 if (!function_exists('wp_remote_post')) {
335 function wp_remote_post($url, $args = [])
336 {
337 return [];
338 }
339 }
340 if (!function_exists('wp_remote_get')) {
341 function wp_remote_get($url, $args = [])
342 {
343 return [];
344 }
345 }
346 if (!function_exists('is_wp_error')) {
347 function is_wp_error($thing)
348 {
349 return false;
350 }
351 }
352 if (!function_exists('wp_remote_retrieve_body')) {
353 function wp_remote_retrieve_body($response)
354 {
355 return '';
356 }
357 }
358
359 // Cron API stubs — namespace-fallback resolution requires these to live in
360 // the global namespace so callers in `SmashBalloon\Reviews\Pro\Services\BulkUpdate`
361 // (and elsewhere) can find them via PHP's fallback lookup.
362 if (!function_exists('wp_schedule_single_event')) {
363 function wp_schedule_single_event($timestamp, $hook, $args = [])
364 {
365 return true;
366 }
367 }
368
369 // Recurring-event recorder so scheduling logic (idempotency, recurrence) is
370 // unit-testable: wp_schedule_event() records into $wp_scheduled_events_mock and
371 // wp_next_scheduled() reads it back. Tests reset the global in setUp().
372 if (!function_exists('wp_schedule_event')) {
373 function wp_schedule_event($timestamp, $recurrence, $hook, $args = [], $wp_error = false)
374 {
375 global $wp_scheduled_events_mock;
376 if (!is_array($wp_scheduled_events_mock)) {
377 $wp_scheduled_events_mock = [];
378 }
379 $wp_scheduled_events_mock[$hook] = [
380 'timestamp' => $timestamp,
381 'recurrence' => $recurrence,
382 'args' => $args,
383 ];
384 return true;
385 }
386 }
387
388 if (!function_exists('wp_next_scheduled')) {
389 function wp_next_scheduled($hook, $args = [])
390 {
391 global $wp_scheduled_events_mock;
392 if (is_array($wp_scheduled_events_mock) && isset($wp_scheduled_events_mock[$hook])) {
393 return $wp_scheduled_events_mock[$hook]['timestamp'];
394 }
395 return false;
396 }
397 }
398
399 if (!function_exists('wp_clear_scheduled_hook')) {
400 function wp_clear_scheduled_hook($hook, $args = [], $wp_error = false)
401 {
402 global $wp_scheduled_events_mock;
403 if (is_array($wp_scheduled_events_mock)) {
404 unset($wp_scheduled_events_mock[$hook]);
405 }
406 return 0;
407 }
408 }
409
410 // Autoloader
411 require_once dirname(__DIR__) . '/vendor/autoload.php';
412