diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index 67318bd..a093b7d 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -20,9 +20,11 @@ // Import commands.js using ES2015 syntax: import "./commands"; -Cypress.on("uncaught:exception", (err, runnable) => { - // returning false here prevents Cypress from failing the test - return false; +import { getCypressUncaughtExceptionDecision } from "./uncaughtExceptionPolicy"; + +Cypress.on("uncaught:exception", (error) => { + // Ignore only known benign browser/runtime noise; fail all other app errors. + return getCypressUncaughtExceptionDecision(error); }); // Alternatively you can use CommonJS syntax: diff --git a/cypress/support/uncaughtExceptionPolicy.js b/cypress/support/uncaughtExceptionPolicy.js new file mode 100644 index 0000000..e02a6b1 --- /dev/null +++ b/cypress/support/uncaughtExceptionPolicy.js @@ -0,0 +1,26 @@ +// The Lucia project. +// Copyright 2026-2026 DSP, inc. All rights reserved. +// Authors: +// imacat.yang@dsp.im (imacat), 2026/03/08 +/** + * Returns whether an uncaught exception should be ignored in Cypress. + * @param {unknown} error - The thrown uncaught exception. + * @returns {boolean} True when the exception is known-benign and safe to ignore. + */ +export function shouldIgnoreUncaughtException(error) { + const message = error instanceof Error ? error.message : String(error); + const ignorePatterns = [ + /ResizeObserver loop limit exceeded/i, + /ResizeObserver loop completed with undelivered notifications/i, + ]; + return ignorePatterns.some((pattern) => pattern.test(message)); +} + +/** + * Converts ignore policy into Cypress uncaught-exception callback return value. + * @param {unknown} error - The thrown uncaught exception. + * @returns {false|undefined} False to ignore, undefined to let Cypress fail the test. + */ +export function getCypressUncaughtExceptionDecision(error) { + return shouldIgnoreUncaughtException(error) ? false : undefined; +} diff --git a/tests/unit/cypress/uncaughtExceptionPolicy.test.js b/tests/unit/cypress/uncaughtExceptionPolicy.test.js new file mode 100644 index 0000000..34c4c12 --- /dev/null +++ b/tests/unit/cypress/uncaughtExceptionPolicy.test.js @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { + getCypressUncaughtExceptionDecision, + shouldIgnoreUncaughtException, +} from "../../../cypress/support/uncaughtExceptionPolicy"; + +describe("shouldIgnoreUncaughtException", () => { + it("returns true for known benign ResizeObserver errors", () => { + expect( + shouldIgnoreUncaughtException(new Error("ResizeObserver loop limit exceeded")), + ).toBe(true); + }); + + it("returns false for unknown runtime errors", () => { + expect(shouldIgnoreUncaughtException(new Error("TypeError: x is undefined"))).toBe(false); + }); + + it("returns false for Cypress handler when the error should be ignored", () => { + expect( + getCypressUncaughtExceptionDecision( + new Error("ResizeObserver loop completed with undelivered notifications."), + ), + ).toBe(false); + }); + + it("returns undefined for Cypress handler when the error should fail the test", () => { + expect( + getCypressUncaughtExceptionDecision(new Error("TypeError: x is undefined")), + ).toBeUndefined(); + }); +});