@@ -22,16 +22,21 @@ class PreReleaseNoticeTest extends TestCase {
2222 * @param bool $can_manage Whether the mock user may manage options.
2323 * @param bool $dismissed Whether the mock user dismissed the notice.
2424 */
25- private function mock_dependencies ( bool $ can_manage = true , bool $ dismissed = false ): void {
25+ private function mock_dependencies ( bool $ can_manage = true , ? string $ dismissed_version = null ): void {
2626 when ( 'esc_html ' )->returnArg ();
2727 when ( 'esc_url ' )->returnArg ();
28+ when ( 'wp_kses_post ' )->returnArg ();
2829 when ( 'wp_nonce_url ' )->returnArg ();
2930 when ( 'admin_url ' )->returnArg ();
3031 when ( 'current_user_can ' )->justReturn ( $ can_manage );
3132 when ( 'get_current_user_id ' )->justReturn ( 1 );
3233 when ( 'get_user_meta ' )->alias (
33- static function ( $ id , $ key ) use ( $ dismissed ) {
34- return $ key === PreReleaseNotice::DISMISSED_META_KEY ? $ dismissed : false ;
34+ static function ( $ id , $ key ) use ( $ dismissed_version ) {
35+ if ( $ key !== PreReleaseNotice::DISMISSED_META_KEY ) {
36+ return false ;
37+ }
38+
39+ return null === $ dismissed_version ? '' : $ dismissed_version ;
3540 }
3641 );
3742 when ( 'plugin_dir_url ' )->returnArg ();
@@ -95,19 +100,19 @@ private function render_for( string $hook_suffix ): string {
95100 public function test_renders_on_the_settings_page (): void {
96101 self ::mock_dependencies ();
97102
98- self ::assertStringContainsString (
99- 'pre-release ' ,
100- self ::render_for ( 'settings_page_antispam_bee ' )
101- );
103+ $ output = self ::render_for ( 'settings_page_antispam_bee ' );
104+
105+ self ::assertStringContainsString ( 'pre-release ' , $ output );
106+ self ::assertStringContainsString ( 'is-dismissible ' , $ output );
107+ self ::assertStringContainsString ( '<code>3.0.0-beta.2</code> ' , $ output );
108+ self ::assertStringNotContainsString ( 'data-antispam-bee-dismiss ' , $ output );
109+ self ::assertStringNotContainsString ( '<button type="button" class="notice-dismiss" ' , $ output );
102110 }
103111
104112 public function test_renders_on_the_plugins_list (): void {
105113 self ::mock_dependencies ();
106114
107- self ::assertStringContainsString (
108- 'pre-release ' ,
109- self ::render_for ( 'plugins.php ' )
110- );
115+ self ::assertStringContainsString ( 'pre-release ' , self ::render_for ( 'plugins.php ' ) );
111116 }
112117
113118 public function test_renders_using_the_global_hook_suffix (): void {
@@ -154,12 +159,18 @@ public function test_does_not_render_without_manage_options_capability(): void {
154159 self ::assertSame ( '' , self ::render_for ( 'plugins.php ' ) );
155160 }
156161
157- public function test_does_not_render_when_dismissed_by_the_user (): void {
158- self ::mock_dependencies ( true , true );
162+ public function test_does_not_render_when_dismissed_for_the_installed_version (): void {
163+ self ::mock_dependencies ( true , ' 3.0.0-beta.2 ' );
159164
160165 self ::assertSame ( '' , self ::render_for ( 'plugins.php ' ) );
161166 }
162167
168+ public function test_renders_again_for_a_newer_prerelease_version (): void {
169+ self ::mock_dependencies ( true , '3.0.0-RC.1 ' );
170+
171+ self ::assertStringContainsString ( 'pre-release ' , self ::render_for ( 'plugins.php ' ) );
172+ }
173+
163174 public function test_enqueues_assets_on_the_plugins_list (): void {
164175 self ::mock_dependencies ();
165176 $ enqueued = [];
@@ -207,12 +218,18 @@ static function ( $id, $key, $value ) use ( &$updated ) {
207218 when ( 'wp_send_json_success ' )->alias (
208219 static function () use ( &$ sent ) {
209220 $ sent = true ;
221+
222+ throw new RuntimeException ( '__ANTISPAM_BEE_EXPECTED_HALT__ ' );
210223 }
211224 );
212225
213- PreReleaseNotice::handle_dismiss ();
226+ self ::assert_and_terminates (
227+ static function () {
228+ PreReleaseNotice::handle_dismiss ();
229+ }
230+ );
214231
215- self ::assertSame ( [ [ 1 , PreReleaseNotice::DISMISSED_META_KEY , 1 ] ], $ updated );
232+ self ::assertSame ( [ [ 1 , PreReleaseNotice::DISMISSED_META_KEY , \ AntispamBee \ PLUGIN_VERSION ] ], $ updated );
216233 self ::assertTrue ( $ sent , 'The AJAX request must be acknowledged ' );
217234 }
218235
@@ -243,14 +260,38 @@ static function () {
243260 }
244261 );
245262
246- self ::assertSame ( [ [ 1 , PreReleaseNotice::DISMISSED_META_KEY , 1 ] ], $ updated );
263+ self ::assertSame ( [ [ 1 , PreReleaseNotice::DISMISSED_META_KEY , \ AntispamBee \ PLUGIN_VERSION ] ], $ updated );
247264 self ::assertSame ( 'https://example.com/wp-admin/plugins.php ' , $ target );
248265 }
249266
250- public function test_handle_dismiss_denies_without_capability (): void {
267+ public function test_handle_dismiss_denies_ajax_without_capability (): void {
251268 self ::mock_dependencies ( false );
252269 when ( 'wp_doing_ajax ' )->justReturn ( true );
253270
271+ $ captured = null ;
272+ when ( 'wp_send_json_error ' )->alias (
273+ static function ( ...$ args ) use ( &$ captured ) {
274+ $ captured = $ args ;
275+
276+ throw new RuntimeException ( '__ANTISPAM_BEE_EXPECTED_HALT__ ' );
277+ }
278+ );
279+
280+ self ::assert_and_terminates (
281+ static function () {
282+ PreReleaseNotice::handle_dismiss ();
283+ }
284+ );
285+
286+ self ::assertNotNull ( $ captured , 'The handler must refuse users without the capability ' );
287+ self ::assertCount ( 2 , $ captured );
288+ self ::assertSame ( 403 , $ captured [1 ], 'The AJAX denial must carry an HTTP 403 status ' );
289+ }
290+
291+ public function test_handle_dismiss_denies_form_request_without_capability (): void {
292+ self ::mock_dependencies ( false );
293+ when ( 'wp_doing_ajax ' )->justReturn ( false );
294+
254295 $ captured = null ;
255296 self ::stub_terminator ( 'wp_die ' , static function ( ...$ args ) use ( &$ captured ) {
256297 $ captured = $ args ;
@@ -263,6 +304,41 @@ static function () {
263304 );
264305
265306 self ::assertNotNull ( $ captured , 'The handler must refuse users without the capability ' );
266- self ::assertSame ( 403 , $ captured [1 ] );
307+ self ::assertCount ( 3 , $ captured );
308+ self ::assertSame ( '' , $ captured [1 ], 'The page title must be an empty string ' );
309+ self ::assertSame ( 403 , $ captured [2 ], 'The non-AJAX denial must carry an HTTP 403 status ' );
310+ }
311+
312+ /**
313+ * Data provider for pre-release detection.
314+ *
315+ * @return array<string, array{string, bool}>
316+ */
317+ public static function data_is_pre_release (): array {
318+ return [
319+ 'stable 3.0.0 ' => [ '3.0.0 ' , false ],
320+ 'stable 1.2.3 ' => [ '1.2.3 ' , false ],
321+ 'stable with prefix ' => [ 'v2.11.13 ' , false ],
322+ 'rc suffix ' => [ '3.0.0-RC.1 ' , true ],
323+ 'beta suffix ' => [ '3.0.0-beta.2 ' , true ],
324+ 'alpha suffix ' => [ '3.0.0-alpha ' , true ],
325+ 'mixed case suffix ' => [ '3.0.0-Rc1 ' , true ],
326+ 'build metadata ' => [ '3.0.0-beta.2+build ' , true ],
327+ 'empty string ' => [ '' , false ],
328+ 'not a version ' => [ 'foo ' , false ],
329+ 'suffix without number ' => [ '-beta ' , false ],
330+ ];
331+ }
332+
333+ /**
334+ * Test is_pre_release() against a range of version strings.
335+ *
336+ * @param string $version Version string.
337+ * @param bool $expected Expected result.
338+ *
339+ * @dataProvider data_is_pre_release
340+ */
341+ public function test_is_pre_release ( string $ version , bool $ expected ): void {
342+ self ::assertSame ( $ expected , PreReleaseNotice::is_pre_release ( $ version ) );
267343 }
268344}
0 commit comments