Skip to content

Administration: Handle AJAX failures for plugin and theme actions - #12654

Draft
ArkaPrabhaChowdhury wants to merge 3 commits into
WordPress:trunkfrom
ArkaPrabhaChowdhury:trac-55368-ajax-failure
Draft

Administration: Handle AJAX failures for plugin and theme actions#12654
ArkaPrabhaChowdhury wants to merge 3 commits into
WordPress:trunkfrom
ArkaPrabhaChowdhury:trac-55368-ajax-failure

Conversation

@ArkaPrabhaChowdhury

@ArkaPrabhaChowdhury ArkaPrabhaChowdhury commented Jul 23, 2026

Copy link
Copy Markdown

What changed

  • Restore plugin and theme deletion controls when an Ajax request fails.
  • Avoid showing an undefined theme deletion error for invalid responses.
  • Restore auto-update toggle labels after failed requests and suggest retrying.
  • Add focused QUnit coverage for maintenance-mode and network failure responses.

Why

Plugin and theme actions can receive an invalid response while maintenance mode is active or when the server is unavailable. The existing handlers could leave controls displaying a permanent loading state or expose undefined as the error message.

Testing

  • grunt jshint:core --file=wp/updates.js
  • grunt jshint:tests
  • npm run typecheck:js
  • QUnit source mode: 541/541 passed
  • QUnit compiled mode: 541/541 passed

Trac ticket: https://core.trac.wordpress.org/ticket/55368

Use of AI Tools

AI assistance: Yes
Tool(s): OpenAI Codex
Model(s): GPT-5
Used for: Reviewing the Trac ticket and relevant code, helping with the initial implementation and regression-test approach, and assisting with validation and PR-description drafting. The final implementation, tests, validation results, and description were reviewed and accepted by me.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Copilot AI review requested due to automatic review settings July 23, 2026 09:47
@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves resilience of wp-admin plugin/theme update interactions by restoring UI controls and providing clearer messaging when AJAX requests fail or return invalid responses (e.g., maintenance mode / network issues).

Changes:

  • Restore plugin/theme deletion UI controls after failed/invalid AJAX responses.
  • Improve auto-update toggle failure messaging and restore toggle labels after failures.
  • Add QUnit coverage for deletion failures and auto-update failure/invalid-response scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
tests/qunit/wp-admin/js/updates.js Adds QUnit tests covering deletion and auto-update failure recovery behaviors.
src/js/_enqueues/wp/updates.js Updates deletion and auto-update handlers to restore UI state and improve failure messaging.
Comments suppressed due to low confidence (2)

src/js/_enqueues/wp/updates.js:1512

  • The delete link is restored with .text() even though originaltext was captured via .html(). Using .html() avoids stripping markup and avoids accidentally clearing the label when originaltext is missing.
		$deleteLink = $plugin.find( '.row-actions a.delete' );
		$deleteLink.text( $deleteLink.data( 'originaltext' ) );

tests/qunit/wp-admin/js/updates.js:208

  • This test stubs wp.ajax.send but never restores it, which can leak into other test files and change global Ajax behavior. Restore the stub at the end of the test.
		sinon.stub( wp.ajax, 'send' ).returns( jQuery.Deferred().promise() );

		wp.updates.deleteTheme( { slug: 'twentyeleven' } );
		wp.ajax.send.firstCall.args[ 0 ].error( 'Briefly unavailable for scheduled maintenance.' );

		assert.strictEqual( $button.text(), 'Delete', 'The delete button text is restored.' );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/js/_enqueues/wp/updates.js Outdated
Comment on lines +136 to +142
sinon.stub( wp.ajax, 'send' ).returns( jQuery.Deferred().promise() );

wp.updates.deletePlugin( { slug: 'jetpack', plugin: 'jetpack/jetpack.php' } );
wp.ajax.send.firstCall.args[ 0 ].error( 'Briefly unavailable for scheduled maintenance.' );

assert.strictEqual( $pluginRow.find( 'a.delete' ).text(), 'Delete', 'The delete link text is restored.' );
} );
Comment on lines +230 to +233
afterEach: function() {
window.pagenow = this.oldPagenow;
}
} );
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 13:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

src/js/_enqueues/wp/updates.js:1512

  • deletePlugin() stores the original delete-link markup via $link.html()/data( 'originaltext' ), but the error handler restores it using .text(), which will strip any markup and can change the link label if it contains HTML (e.g., screen-reader text). Restore with .html() for consistency and to avoid altering the original content.
		$deleteLink = $plugin.find( '.row-actions a.delete' );
		$deleteLink.text( $deleteLink.data( 'originaltext' ) );

tests/qunit/wp-admin/js/updates.js:139

  • This test stubs wp.ajax.send but never restores it, which can leak into later tests and change behavior (other QUnit tests in this suite typically restore stubs in afterEach, e.g. tests/qunit/wp-admin/js/customize-header.js:10-12). Please restore the stub at the end of the test.
		sinon.stub( wp.ajax, 'send' ).returns( jQuery.Deferred().promise() );

		wp.updates.deletePlugin( { slug: 'jetpack', plugin: 'jetpack/jetpack.php' } );
		wp.ajax.send.firstCall.args[ 0 ].error( 'Briefly unavailable for scheduled maintenance.' );

tests/qunit/wp-admin/js/updates.js:232

  • beforeEach stubs jQuery.post, but afterEach does not restore it. This can pollute subsequent tests (similar modules in this test suite restore their stubs/spies in afterEach).
		afterEach: function() {
			window.pagenow = this.oldPagenow;
		}

Comment thread tests/qunit/wp-admin/js/updates.js Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 13:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

tests/qunit/wp-admin/js/updates.js:235

  • jQuery.post is stubbed in beforeEach but never restored in afterEach, which can leak into other QUnit files and break unrelated tests. Restore the stub in afterEach.
	QUnit.module( 'wp.updates.autoUpdates', {
		beforeEach: function() {
			this.oldPagenow = window.pagenow;
			window.pagenow = 'plugins';
			this.request = jQuery.Deferred();
			sinon.stub( jQuery, 'post' ).returns( this.request.promise() );
			this.$column = $(
				'<div class="column-auto-updates">' +
					'<div class="notice notice-error hidden"><p></p></div>' +
					'<button class="toggle-auto-update" data-wp-action="enable">' +
						'<span class="dashicons-update hidden"></span>' +
						'<span class="label">Enable auto-updates</span>' +
					'</button>' +
				'</div>'
			).appendTo( '#qunit-fixture' );
		},
		afterEach: function() {
			window.pagenow = this.oldPagenow;
		}
	} );

tests/qunit/wp-admin/js/updates.js:142

  • This test stubs wp.ajax.send but never restores it, which can leak into subsequent tests/files and cause false failures (or hide real ones). Restore the stub at the end of the test (or in a module afterEach).
		sinon.stub( wp.ajax, 'send' ).returns( jQuery.Deferred().promise() );

		wp.updates.deletePlugin( { slug: 'jetpack', plugin: 'jetpack/jetpack.php' } );
		wp.ajax.send.firstCall.args[ 0 ].error( 'Briefly unavailable for scheduled maintenance.' );

		assert.strictEqual( $pluginRow.find( 'a.delete' ).text(), 'Delete', 'The delete link text is restored.' );
	} );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants