Add flag to send HTML or plain Text to banner (#947)

This commit is contained in:
Fábio Rosado
2022-11-14 18:36:54 +00:00
committed by GitHub
parent adfa9a9b05
commit da2728e6df
4 changed files with 97 additions and 61 deletions

View File

@@ -13,7 +13,11 @@ export class FetchError extends Error {
}
}
export function _createAlertBanner(message: string, level: "error" | "warning" = "error", logMessage = true) {
export function _createAlertBanner(
message: string,
level: "error" | "warning" = "error",
messageType: "text" | "html" = "text",
logMessage = true) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
switch (`log-${level}-${logMessage}`) {
case "log-error-true":
@@ -26,21 +30,27 @@ export function _createAlertBanner(message: string, level: "error" | "warning" =
const banner = document.createElement("div")
banner.className = `alert-banner py-${level}`
banner.innerHTML = message
if (messageType === "html") {
banner.innerHTML = message;
}
else {
banner.textContent = message;
}
if (level === "warning") {
const closeButton = document.createElement("button")
const closeButton = document.createElement("button");
closeButton.id = "alert-close-button"
closeButton.addEventListener("click", () => {
banner.remove()
banner.remove();
})
closeButton.innerHTML = CLOSEBUTTON
closeButton.innerHTML = CLOSEBUTTON;
banner.appendChild(closeButton)
banner.appendChild(closeButton);
}
document.body.prepend(banner)
document.body.prepend(banner);
}
/*
@@ -59,10 +69,10 @@ export function withUserErrorHandler(fn) {
* PyScript or Pyodide during loading. Probably not be used for issues that occur within
* Python scripts, since stderr can be routed to somewhere in the DOM
*/
_createAlertBanner(error.message)
_createAlertBanner(error.message);
}
else {
throw error
throw error;
}
}
}

View File

@@ -102,7 +102,7 @@ export class PyScriptApp {
// is too messy to implement it reliably. We might want to revisit
// this once it's in a better shape.
showWarning(
'Multiple <py-config> tags detected. Only the first is ' +
'Multiple <py-config> tags detected. Only the first is ' +
'going to be parsed, all the others will be ignored',
);
}
@@ -127,7 +127,7 @@ export class PyScriptApp {
}
if (this.config.runtimes.length > 1) {
showWarning('Multiple runtimes are not supported yet.<br />Only the first will be used');
showWarning('Multiple runtimes are not supported yet.<br />Only the first will be used', "html");
}
const runtime_cfg = this.config.runtimes[0];
this.runtime = new PyodideRuntime(this.config,

View File

@@ -41,8 +41,8 @@ export function ensureUniqueId(el: HTMLElement) {
if (el.id === '') el.id = `py-internal-${_uniqueIdCounter++}`;
}
export function showWarning(msg: string): void {
_createAlertBanner(msg, "warning")
export function showWarning(msg: string, messageType: "text" | "html" = "text"): void {
_createAlertBanner(msg, "warning", messageType);
}
export function handleFetchError(e: Error, singleFile: string) {
@@ -69,8 +69,8 @@ export function handleFetchError(e: Error, singleFile: string) {
// We need to create the banner because `handleFetchError` is called before we
// use withUserErrorHandler in main.js we are also disabling the log message
// because it will be logged by the uncaught exception in promise.
_createAlertBanner(errorContent, "error", false)
throw new UserError(errorContent)
_createAlertBanner(errorContent, "error", "html", false);
throw new UserError(errorContent);
}
export function readTextFromPath(path: string) {

View File

@@ -1,84 +1,84 @@
import { jest } from "@jest/globals"
import { expect, it, jest } from "@jest/globals"
import { _createAlertBanner, withUserErrorHandler, UserError } from "../../src/exceptions"
describe("Test _createAlertBanner", () => {
afterEach(() => {
// Ensure we always have a clean body
document.body.innerHTML = `<div>Hello World</div>`
document.body.innerHTML = `<div>Hello World</div>`;
})
it("error level shouldn't contain close button", async () => {
_createAlertBanner("Something went wrong!", "error")
_createAlertBanner("Something went wrong!", "error");
const banner = document.getElementsByClassName("alert-banner")
const closeButton = document.getElementById("alert-close-button")
expect(banner.length).toBe(1)
expect(banner[0].innerHTML).toBe("Something went wrong!")
expect(closeButton).toBeNull()
const banner = document.getElementsByClassName("alert-banner");
const closeButton = document.getElementById("alert-close-button");
expect(banner.length).toBe(1);
expect(banner[0].innerHTML).toBe("Something went wrong!");
expect(closeButton).toBeNull();
})
it("warning level should contain close button", async () => {
_createAlertBanner("This is a warning", "warning")
_createAlertBanner("This is a warning", "warning");
const banner = document.getElementsByClassName("alert-banner")
const closeButton = document.getElementById("alert-close-button")
expect(banner.length).toBe(1)
expect(banner[0].innerHTML).toContain("This is a warning")
expect(closeButton).not.toBeNull()
const banner = document.getElementsByClassName("alert-banner");
const closeButton = document.getElementById("alert-close-button");
expect(banner.length).toBe(1);
expect(banner[0].innerHTML).toContain("This is a warning");
expect(closeButton).not.toBeNull();
})
it("error level banner should log to console", async () => {
const logSpy = jest.spyOn(console, "error")
const logSpy = jest.spyOn(console, "error");
_createAlertBanner("Something went wrong!")
_createAlertBanner("Something went wrong!");
expect(logSpy).toHaveBeenCalledWith("Something went wrong!")
expect(logSpy).toHaveBeenCalledWith("Something went wrong!");
})
it("warning level banner should log to console", async () => {
const logSpy = jest.spyOn(console, "warn")
const logSpy = jest.spyOn(console, "warn");
_createAlertBanner("This warning", "warning")
_createAlertBanner("This warning", "warning");
expect(logSpy).toHaveBeenCalledWith("This warning")
expect(logSpy).toHaveBeenCalledWith("This warning");
})
it("close button should remove element from page", async () => {
let banner = document.getElementsByClassName("alert-banner")
expect(banner.length).toBe(0)
let banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(0);
_createAlertBanner("Warning!", "warning")
_createAlertBanner("Warning!", "warning");
// Just a sanity check
banner = document.getElementsByClassName("alert-banner")
expect(banner.length).toBe(1)
banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(1);
const closeButton = document.getElementById("alert-close-button")
const closeButton = document.getElementById("alert-close-button");
if(closeButton) {
closeButton.click()
closeButton.click();
// Confirm that clicking the close button, removes the element
banner = document.getElementsByClassName("alert-banner")
expect(banner.length).toBe(0)
banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(0);
} else {
fail("Unable to find close button on the page, but should exist")
fail("Unable to find close button on the page, but should exist");
}
})
it("toggling logging off on error alert shouldn't log to console", async () => {
const errorLogSpy = jest.spyOn(console, "error")
const errorLogSpy = jest.spyOn(console, "error");
_createAlertBanner("Test error", "error", false)
expect(errorLogSpy).not.toHaveBeenCalledWith("Test error")
_createAlertBanner("Test error", "error", "text", false);
expect(errorLogSpy).not.toHaveBeenCalledWith("Test error");
})
it("toggling logging off on warning alert shouldn't log to console", async () => {
const warnLogSpy = jest.spyOn(console, "warn")
_createAlertBanner("Test warning", "warning", false)
expect(warnLogSpy).not.toHaveBeenCalledWith("Test warning")
const warnLogSpy = jest.spyOn(console, "warn");
_createAlertBanner("Test warning", "warning", "text", false);
expect(warnLogSpy).not.toHaveBeenCalledWith("Test warning");
})
})
@@ -87,31 +87,57 @@ describe("Test withUserErrorHandler", () => {
afterEach(() => {
// Ensure we always have a clean body
document.body.innerHTML = `<div>Hello World</div>`
document.body.innerHTML = `<div>Hello World</div>`;
})
it("userError doesn't stop execution", async () => {
function exception() {
throw new UserError("Computer says no")
throw new UserError("Computer says no");
}
function func() {
withUserErrorHandler(exception)
return "Hello, world"
withUserErrorHandler(exception);
return "Hello, world";
}
const returnValue = func()
const banners = document.getElementsByClassName("alert-banner")
expect(banners.length).toBe(1)
expect(banners[0].innerHTML).toBe("Computer says no")
expect(returnValue).toBe("Hello, world")
const returnValue = func();
const banners = document.getElementsByClassName("alert-banner");
expect(banners.length).toBe(1);
expect(banners[0].innerHTML).toBe("Computer says no");
expect(returnValue).toBe("Hello, world");
})
it("any other exception should stop execution and raise", async () => {
function exception() {
throw new Error("Explosions!")
throw new Error("Explosions!");
}
expect(() => withUserErrorHandler(exception)).toThrow(new Error("Explosions!"))
})
it('_createAlertbanner messageType text writes message to content', async () => {
let banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(0);
const message = '<p>Test message</p>'
_createAlertBanner(message, 'error', 'text');
banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(1);
expect(banner[0].innerHTML).toBe("&lt;p&gt;Test message&lt;/p&gt;");
expect(banner[0].textContent).toBe(message);
})
it('_createAlertbanner messageType html writes message to innerHTML', async () => {
let banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(0);
const message = '<p>Test message</p>';
_createAlertBanner(message, 'error', 'html');
banner = document.getElementsByClassName("alert-banner");
expect(banner.length).toBe(1);
expect(banner[0].innerHTML).toBe(message);
expect(banner[0].textContent).toBe("Test message");
})
})