-
Notifications
You must be signed in to change notification settings - Fork 220
Translation Events: Show a notice after trashing, restoring, or deleting an event #891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ekamran
wants to merge
2
commits into
WordPress:trunk
Choose a base branch
from
ekamran:fix/event-action-notices
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
260 changes: 260 additions & 0 deletions
260
...c_html/wp-content/plugins/wporg-gp-translation-events/tests/routes/Event_Notices_Test.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| <?php | ||
| /** | ||
| * Tests that trashing, restoring, and deleting an event leave a notice | ||
| * behind for the page the user is redirected to. | ||
| * | ||
| * @package wporg-gp-translation-events | ||
| */ | ||
|
|
||
| declare( strict_types = 1 ); | ||
|
|
||
| use Wporg\Tests\Base_Test; | ||
| use Wporg\TranslationEvents\Event\Event_Form_Handler; | ||
| use Wporg\TranslationEvents\Routes\Event\Delete_Route; | ||
| use Wporg\TranslationEvents\Routes\Event\Trash_Route; | ||
| use Wporg\TranslationEvents\Tests\Event_Factory; | ||
| use Wporg\TranslationEvents\Translation_Events; | ||
|
|
||
| /** | ||
| * Trashing, restoring, and permanently deleting an event all redirect away from | ||
| * the page the action was triggered from. Each one should leave a GlotPress | ||
| * notice behind so the user is told what happened after the redirect, rather | ||
| * than landing on a new page with no feedback (see issue #372). | ||
| * | ||
| * GlotPress notices are carried across the redirect in `_gp_notice_<key>` | ||
| * cookies. These tests capture those cookies through the `gp_set_cookie` | ||
| * filter, which also suppresses the real `setcookie()` call so it does not | ||
| * warn about headers already sent under PHPUnit. | ||
| */ | ||
| class Event_Notices_Test extends Base_Test { | ||
| /** | ||
| * The notice messages captured from the `gp_set_cookie` filter, keyed by | ||
| * the GlotPress notice key ('notice' or 'error'). | ||
| * | ||
| * @var array<string, string> | ||
| */ | ||
| private array $notices = array(); | ||
|
|
||
| /** | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| * The `gp_set_cookie` filter callback that captures notice cookies. | ||
| * | ||
| * @var callable | ||
| */ | ||
| private $cookie_capture; | ||
|
|
||
| /** | ||
| * The `wp_redirect` filter callback that stops redirect headers under PHPUnit. | ||
| * | ||
| * @var callable | ||
| */ | ||
| private $redirect_guard; | ||
|
|
||
| /** | ||
| * Registers the cookie capture and redirect guard before each test. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function setUp(): void { | ||
| parent::setUp(); | ||
|
|
||
| // Any logged-in user can manage events, so the capability checks pass | ||
| // and the redirect path under test is reached. | ||
| add_filter( 'gp_translation_events_can_crud_event', '__return_true' ); | ||
|
|
||
| $this->cookie_capture = function ( $args ) { | ||
| // GlotPress stores notices in `_gp_notice_<key>` cookies; capture | ||
| // the message under its key so both 'notice' and 'error' are seen. | ||
| if ( isset( $args[0], $args[1] ) && 0 === strpos( (string) $args[0], '_gp_notice_' ) ) { | ||
| $key = substr( (string) $args[0], strlen( '_gp_notice_' ) ); | ||
| $this->notices[ $key ] = $args[1]; | ||
| } | ||
|
|
||
| // Returning false stops GlotPress from actually calling setcookie(). | ||
| return false; | ||
| }; | ||
| add_filter( 'gp_set_cookie', $this->cookie_capture ); | ||
|
|
||
| // Stop the redirect from emitting a header under PHPUnit. | ||
| $this->redirect_guard = '__return_false'; | ||
| add_filter( 'wp_redirect', $this->redirect_guard ); | ||
| } | ||
|
|
||
| /** | ||
| * Removes the filters registered in setUp(). | ||
| * | ||
| * @return void | ||
| */ | ||
| public function tearDown(): void { | ||
| remove_filter( 'wp_redirect', $this->redirect_guard ); | ||
| remove_filter( 'gp_set_cookie', $this->cookie_capture ); | ||
| remove_filter( 'gp_translation_events_can_crud_event', '__return_true' ); | ||
| unset( $_GET['_wpnonce'] ); | ||
|
|
||
| parent::tearDown(); | ||
| } | ||
|
|
||
| /** | ||
| * Trashing an event through the trash route sets a success notice. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function test_trashing_an_event_sets_a_notice() { | ||
| $event_id = ( new Event_Factory() )->create_active( $this->now ); | ||
|
|
||
| $this->run_route( Trash_Route::class, $event_id, 'trash_translation_event_' . $event_id ); | ||
|
|
||
| $this->assertSame( 'Event moved to the trash.', $this->notices['notice'] ?? null ); | ||
| } | ||
|
|
||
| /** | ||
| * Restoring a trashed event through the trash route sets a success notice. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function test_restoring_an_event_sets_a_notice() { | ||
| $factory = new Event_Factory(); | ||
| $event_id = $factory->create_active( $this->now ); | ||
| Translation_Events::get_event_repository()->trash_event( | ||
| Translation_Events::get_event_repository()->get_event( $event_id ) | ||
| ); | ||
|
|
||
| // The trash route toggles: a trashed event is restored. | ||
| $this->run_route( Trash_Route::class, $event_id, 'trash_translation_event_' . $event_id ); | ||
|
|
||
| $this->assertSame( 'Event restored.', $this->notices['notice'] ?? null ); | ||
| } | ||
|
|
||
| /** | ||
| * Permanently deleting a trashed event sets a success notice. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function test_permanently_deleting_an_event_sets_a_notice() { | ||
| $event_id = ( new Event_Factory() )->create_active( $this->now ); | ||
| Translation_Events::get_event_repository()->trash_event( | ||
| Translation_Events::get_event_repository()->get_event( $event_id ) | ||
| ); | ||
|
|
||
| $this->run_route( Delete_Route::class, $event_id, 'delete_translation_event_' . $event_id ); | ||
|
|
||
| $this->assertSame( 'Event permanently deleted.', $this->notices['notice'] ?? null ); | ||
| } | ||
|
|
||
| /** | ||
| * The "Delete event" button on the edit form trashes over AJAX and then | ||
| * redirects the browser to My Events, so the same notice must be set there. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function test_trashing_via_the_form_handler_sets_a_notice() { | ||
| $event_id = ( new Event_Factory() )->create_active( $this->now ); | ||
| wp_set_current_user( $this->factory->user->create() ); | ||
|
|
||
| $form_data = array( | ||
| 'form_name' => 'trash_event', | ||
| 'event_id' => (string) $event_id, | ||
| '_event_nonce' => wp_create_nonce( '_event_nonce' ), | ||
| ); | ||
|
|
||
| // The handler ends in wp_send_json_success(), which calls wp_die(). | ||
| // Route that through the AJAX die handler and make it throw so the | ||
| // notice set just before it can be asserted. | ||
| add_filter( 'wp_doing_ajax', '__return_true' ); | ||
| $die_handler = function () { | ||
| return function () { | ||
| throw new Exception( 'wp_send_json' ); | ||
| }; | ||
| }; | ||
| add_filter( 'wp_die_ajax_handler', $die_handler ); | ||
|
|
||
| $handler = new Event_Form_Handler( $this->now, Translation_Events::get_event_repository() ); | ||
|
|
||
| ob_start(); | ||
| try { | ||
| $handler->handle( $form_data ); | ||
| } catch ( Exception $e ) { | ||
| // Expected: wp_send_json_success() reached wp_die(). | ||
| $this->assertSame( 'wp_send_json', $e->getMessage() ); | ||
| } finally { | ||
| ob_end_clean(); | ||
| remove_filter( 'wp_die_ajax_handler', $die_handler ); | ||
| remove_filter( 'wp_doing_ajax', '__return_true' ); | ||
| } | ||
|
|
||
| $this->assertSame( 'Event moved to the trash.', $this->notices['notice'] ?? null ); | ||
| } | ||
|
|
||
| /** | ||
| * When the trash fails, the user is still redirected to My Events, so the | ||
| * failure must be surfaced there as an error notice. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function test_a_failed_trash_via_the_form_handler_sets_an_error_notice() { | ||
| $event_id = ( new Event_Factory() )->create_active( $this->now ); | ||
| wp_set_current_user( $this->factory->user->create() ); | ||
|
|
||
| $form_data = array( | ||
| 'form_name' => 'trash_event', | ||
| 'event_id' => (string) $event_id, | ||
| '_event_nonce' => wp_create_nonce( '_event_nonce' ), | ||
| ); | ||
|
|
||
| // Force the trash to fail so the error branch is exercised. | ||
| add_filter( 'pre_trash_post', '__return_false' ); | ||
|
|
||
| add_filter( 'wp_doing_ajax', '__return_true' ); | ||
| $die_handler = function () { | ||
| return function () { | ||
| throw new Exception( 'wp_send_json' ); | ||
| }; | ||
| }; | ||
| add_filter( 'wp_die_ajax_handler', $die_handler ); | ||
|
|
||
| $handler = new Event_Form_Handler( $this->now, Translation_Events::get_event_repository() ); | ||
|
|
||
| ob_start(); | ||
| try { | ||
| $handler->handle( $form_data ); | ||
| } catch ( Exception $e ) { | ||
| $this->assertSame( 'wp_send_json', $e->getMessage() ); | ||
| } finally { | ||
| ob_end_clean(); | ||
| remove_filter( 'wp_die_ajax_handler', $die_handler ); | ||
| remove_filter( 'wp_doing_ajax', '__return_true' ); | ||
| remove_filter( 'pre_trash_post', '__return_false' ); | ||
| } | ||
|
|
||
| $this->assertSame( 'Failed to delete event.', $this->notices['error'] ?? null ); | ||
| $this->assertArrayNotHasKey( 'notice', $this->notices ); | ||
| } | ||
|
|
||
| /** | ||
| * Run a route to completion with a valid nonce as a logged-in user. | ||
| * | ||
| * @param string $route_class Route to exercise. | ||
| * @param int $event_id Event the route acts on. | ||
| * @param string $nonce_action Nonce action the route verifies. | ||
| * @return void | ||
| */ | ||
| private function run_route( string $route_class, int $event_id, string $nonce_action ) { | ||
| wp_set_current_user( $this->factory->user->create() ); | ||
| $_GET['_wpnonce'] = wp_create_nonce( $nonce_action ); | ||
|
|
||
| $route = new $route_class(); | ||
| $route->fake_request = true; | ||
|
|
||
| // The route renders a redirect template on success, which would | ||
| // otherwise garble PHPUnit's output. On success it also calls exit_(), | ||
| // which throws under a faked request to halt the way a real exit would. | ||
| ob_start(); | ||
| try { | ||
| $route->handle( $event_id ); | ||
| } catch ( GP_Route_Exit_Exception $e ) { | ||
| // Expected: the route reached its exit after redirecting. | ||
| $this->assertInstanceOf( GP_Route_Exit_Exception::class, $e ); | ||
| } finally { | ||
| ob_end_clean(); | ||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.