diff --git a/examples/reproducers/issue-channel-moderation-test-crash/README.md b/examples/reproducers/issue-channel-moderation-test-crash/README.md new file mode 100644 index 00000000..c70b0da7 --- /dev/null +++ b/examples/reproducers/issue-channel-moderation-test-crash/README.md @@ -0,0 +1,41 @@ +# Reproducer for Channel Moderation Test Crash + +This reproducer demonstrates a bug in the test harness setup used in `tests/app/channel-profile-moderation.test.mjs`. + +## Issue Description + +The test helper `createModerationAppHarness` in `tests/helpers/moderation-test-helpers.mjs` instantiates the `Application` class using `Object.create(Application.prototype)`, bypassing the constructor. + +The `Application` constructor is responsible for initializing `this.moderationDecorator`. + +When `app.decorateVideoModeration()` is called (which is done in `renderChannelVideosFromList`), it attempts to call `this.moderationDecorator.decorateVideo()`. Since `this.moderationDecorator` is undefined, it throws a `TypeError: Cannot read properties of undefined (reading 'decorateVideo')`. + +This causes tests in `tests/app/channel-profile-moderation.test.mjs` to fail with exit code 1. + +## How to Run + +Run the reproducer script: + +```bash +node examples/reproducers/issue-channel-moderation-test-crash/repro.mjs +``` + +A successful run (reproduction of the crash) will output: +`SUCCESS: Caught expected error: Cannot read properties of undefined (reading 'decorateVideo')` + +## Fix (Recommendation) + +The `createModerationAppHarness` helper should ensure `moderationDecorator` is initialized on the mock `Application` instance, or use a method of instantiation that runs the constructor (if feasible with mocks). + +Example fix in `tests/helpers/moderation-test-helpers.mjs`: + +```javascript + // ... inside createModerationAppHarness + const app = Object.create(Application.prototype); + // ... + // Manually initialize moderationDecorator since constructor is skipped + app.moderationDecorator = new ModerationDecorator({ + getProfileCacheEntry: (pubkey) => app.getProfileCacheEntry(pubkey), + }); + // ... +``` diff --git a/examples/reproducers/issue-channel-moderation-test-crash/repro.mjs b/examples/reproducers/issue-channel-moderation-test-crash/repro.mjs new file mode 100644 index 00000000..481db285 --- /dev/null +++ b/examples/reproducers/issue-channel-moderation-test-crash/repro.mjs @@ -0,0 +1,36 @@ +import { JSDOM } from "jsdom"; +import "../../../tests/test-helpers/setup-localstorage.mjs"; + +// Mock globals for Application load +const dom = new JSDOM("", { + url: "https://example.com", +}); +globalThis.window = dom.window; +globalThis.document = dom.window.document; +globalThis.HTMLElement = dom.window.HTMLElement; +// Override WebSocket if setup-localstorage didn't do it or if we need a close method +globalThis.WebSocket = class { close() {} }; + +// Import Application +const { Application } = await import("../../../js/app.js"); + +console.log("Creating Application instance bypassing constructor..."); +// This replicates what createModerationAppHarness does in tests/helpers/moderation-test-helpers.mjs +const app = Object.create(Application.prototype); + +// Attempt to call decorateVideoModeration which relies on this.moderationDecorator +// initialized in constructor. +try { + console.log("Attempting to call decorateVideoModeration..."); + app.decorateVideoModeration({}); + console.log("FAIL: decorateVideoModeration did not throw."); + process.exit(1); +} catch (error) { + if (error instanceof TypeError && error.message.includes("decorateVideo")) { + console.log("SUCCESS: Caught expected error:", error.message); + process.exit(0); // Exit success + } else { + console.log("FAIL: Caught unexpected error:", error); + process.exit(1); + } +}