DEV: Show theme/plugin error banner for route loading failures (#24218)

This aims to help admins and developers identify the cause of loading issues on routes.

As with other theme/plugin errors, the UI banner is only shown to administrators. For non-admins, the information is only written to the browser console.
This commit is contained in:
David Taylor 2023-11-02 15:45:02 +00:00 committed by GitHub
parent 08e2ee3ec1
commit 67bcef3959
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 5 deletions

View File

@ -8,6 +8,7 @@ import { setting } from "discourse/lib/computed";
import cookie from "discourse/lib/cookie";
import logout from "discourse/lib/logout";
import mobile from "discourse/lib/mobile";
import identifySource, { consolePrefix } from "discourse/lib/source-identifier";
import DiscourseURL from "discourse/lib/url";
import Category from "discourse/models/category";
import Composer from "discourse/models/composer";
@ -39,6 +40,7 @@ const ApplicationRoute = DiscourseRoute.extend({
loadingSlider: service(),
router: service(),
siteSettings: service(),
clientErrorHandler: service(),
get includeExternalLoginMethods() {
return (
@ -117,15 +119,24 @@ const ApplicationRoute = DiscourseRoute.extend({
const xhrOrErr = err.jqXHR ? err.jqXHR : err;
const exceptionController = this.controllerFor("exception");
const c = window.console;
if (c && c.error) {
c.error(xhrOrErr);
}
const themeOrPluginSource = identifySource(err);
// eslint-disable-next-line no-console
console.error(
...[consolePrefix(err, themeOrPluginSource), xhrOrErr].filter(Boolean)
);
if (xhrOrErr && xhrOrErr.status === 404) {
return this.router.transitionTo("exception-unknown");
}
if (themeOrPluginSource) {
this.clientErrorHandler.displayErrorNotice(
"Error loading route",
themeOrPluginSource
);
}
exceptionController.setProperties({
lastTransition: transition,
thrown: xhrOrErr,

View File

@ -83,11 +83,15 @@ export default class ClientErrorHandlerService extends Service {
let html = `⚠️ ${escape(message)}`;
if (source && source.type === "theme") {
if (source?.type === "theme") {
html += `<br/>${I18n.t("themes.error_caused_by", {
name: escape(source.name),
path: source.path,
})}`;
} else if (source?.type === "plugin") {
html += `<br/>${I18n.t("broken_plugin_alert", {
name: escape(source.name),
})}`;
}
html += `<br/><span class='theme-error-suffix'>${I18n.t(

View File

@ -0,0 +1,30 @@
import { getOwner } from "@ember/application";
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import Sinon from "sinon";
import { acceptance } from "../helpers/qunit-helpers";
acceptance("client-error-handler service", function (needs) {
needs.user({
admin: true,
});
test("displays route-loading errors caused by themes", async function (assert) {
const fakeError = new Error("Something bad happened");
fakeError.stack = "assets/plugins/some-fake-plugin-name.js";
const topicRoute = getOwner(this).lookup("route:topic");
Sinon.stub(topicRoute, "model").throws(fakeError);
const consoleStub = Sinon.stub(console, "error");
try {
await visit("/t/280");
} catch {}
consoleStub.restore();
assert.dom(".broken-theme-alert-banner").exists();
assert
.dom(".broken-theme-alert-banner")
.containsText("some-fake-plugin-name");
});
});

View File

@ -221,6 +221,8 @@ en:
broken_decorator_alert: "Posts may not display correctly because one of the post content decorators on your site raised an error."
broken_plugin_alert: "Caused by plugin '%{name}'"
s3:
regions:
ap_northeast_1: "Asia Pacific (Tokyo)"