Skip to content
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

#8063: improve user-visible error message for InsertAtCursorEffect brick #8069

Merged
merged 4 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/bricks/effects/InsertAtCursorEffect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
*/

import InsertAtCursorEffect from "@/bricks/effects/InsertAtCursorEffect";
import { unsafeAssumeValidArg } from "@/runtime/runtimeTypes";
import { brickOptionsFactory } from "@/testUtils/factories/runtimeFactories";
import { BusinessError } from "@/errors/businessErrors";

const brick = new InsertAtCursorEffect();

Expand All @@ -27,4 +30,18 @@ describe("InsertAtCursorEffect", () => {
it("is root-aware", async () => {
await expect(brick.isRootAware()).resolves.toBe(false);
});

it("throws business error if insert fails", async () => {
// `jsdom` doesn't implement execCommand
document.execCommand = jest.fn().mockReturnValue(false);

await expect(
brick.run(
unsafeAssumeValidArg({
text: "test",
}),
brickOptionsFactory(),
),
).rejects.toThrow(BusinessError);
});
});
19 changes: 17 additions & 2 deletions src/bricks/effects/InsertAtCursorEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ import { type Schema } from "@/types/schemaTypes";
import { isEmpty } from "lodash";
import selectionController from "@/utils/selectionController";
import type { PlatformCapability } from "@/platform/capabilities";
import { insertAtCursorWithCustomEditorSupport } from "@/contentScript/textEditorDom";
import {
ExecCommandError,
insertAtCursorWithCustomEditorSupport,
} from "@/contentScript/textEditorDom";
import { propertiesToSchema } from "@/utils/schemaUtils";
import { expectContext } from "@/utils/expectContext";
import { BusinessError } from "@/errors/businessErrors";

/**
* Insert text at the cursor position. For use with text snippets, etc.
Expand Down Expand Up @@ -75,7 +79,18 @@ class InsertAtCursorEffect extends EffectABC {
// https://github.com/pixiebrix/pixiebrix-extension/pull/7827#issuecomment-1979884573
selectionController.restoreWithoutClearing();

await insertAtCursorWithCustomEditorSupport(text);
try {
await insertAtCursorWithCustomEditorSupport(text);
} catch (error) {
if (error instanceof ExecCommandError) {
throw new BusinessError(
"Error inserting text at cursor. Ensure there is a focused editor in the target frame",
{ cause: error },
);
}

throw error;
}
}
}

Expand Down
22 changes: 21 additions & 1 deletion src/contentScript/textEditorDom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ import { expectContext } from "@/utils/expectContext";
import { dispatchPasteForDraftJs } from "@/utils/domFieldUtils";
import focusController from "@/utils/focusController";

/**
* Error to be thrown when document.execCommand fails.
*
* Treated as application error, callers should convert into a BusinessError subclass where appropriate.
*
* @see document.execCommand
*/
export class ExecCommandError extends Error {
override name = "ExecCommandError";
readonly commandId: string;

constructor(message: string, { commandId }: { commandId: string }) {
super(message);
this.commandId = commandId;
}
}

/**
* @file Text Editor DOM utilities that might call the pageScript.
*
Expand All @@ -39,6 +56,7 @@ import focusController from "@/utils/focusController";
* - Plain content editable (Gmail, etc.)
* - CKEditor 4/5
*
* @throws {ExecCommandError} if the text could not be inserted with InsertTextError
*/
export async function insertAtCursorWithCustomEditorSupport(text: string) {
expectContext(
Expand Down Expand Up @@ -69,6 +87,8 @@ export async function insertAtCursorWithCustomEditorSupport(text: string) {
focusController.restoreWithoutClearing();

if (!document.execCommand("insertText", false, text)) {
throw new Error("Failed to insert text using execCommand");
throw new ExecCommandError("Failed to insert text using execCommand", {
commandId: "insertText",
});
}
}