diff --git a/.changeset/name-failed-assets.md b/.changeset/name-failed-assets.md new file mode 100644 index 0000000..44796c5 --- /dev/null +++ b/.changeset/name-failed-assets.md @@ -0,0 +1,5 @@ +--- +'@livekit/track-processors': patch +--- + +Reject with an `Error` naming the URL when the MediaPipe fileset or a background image fails to load, instead of a bare DOM `Event` diff --git a/e2e/README.md b/e2e/README.md index 99ce1f1..e6d4776 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -33,14 +33,14 @@ Chrome only for now. Firefox, WebKit and BrowserStack devices need their own cal ## What's covered -Seventeen cases across eleven concerns. +Twenty cases. | Spec | IDs | Concern | | --- | --- | --- | | `capability.spec.ts` | A1, A1b, A3, A4 | Support predicates; both pipelines selected and working | -| `lifecycle.spec.ts` | B1, B2 | Attach produces blurred frames; detach restores them | +| `lifecycle.spec.ts` | B1, B2, B3, B4 | Attach produces blurred frames; detach restores them and releases the WebGL context; a missing fileset rejects with an `Error` naming it | | `modes.spec.ts` | C1, C1b, C3 | Switching lands correctly, publishes no intermediate frame, and `disabled` is true passthrough | -| `background.spec.ts` | D1, D5, D6 | Image composited behind the subject; fetched once; a failing image at construction is logged | +| `background.spec.ts` | D1, D5, D6 | Image composited behind the subject; fetched once; a failing image at construction is logged by name | | `geometry.spec.ts` | E1 | Output dimensions follow the source at three resolutions | | `performance.spec.ts` | G1 | `onFrameProcessed` timings are coherent at a usable frame rate | diff --git a/e2e/specs/background.spec.ts b/e2e/specs/background.spec.ts index 4827a0e..3c6ad04 100644 --- a/e2e/specs/background.spec.ts +++ b/e2e/specs/background.spec.ts @@ -133,5 +133,11 @@ test.describe('virtual background', () => { // uncaught error with no message. expect(app.pageErrors, 'uncaught page errors').toEqual([]); expect(await page.evaluate(() => window.harness.lastError()), 'harness logged an error').toBeNull(); + + // #142: the logged failure names the image rather than printing a bare DOM Event. + expect( + app.consoleErrors.some((line) => line.includes('missing-bg.png')), + `no console error named the image: ${JSON.stringify(app.consoleErrors)}`, + ).toBe(true); }); }); diff --git a/e2e/specs/lifecycle.spec.ts b/e2e/specs/lifecycle.spec.ts index 40064bf..9e4ba9b 100644 --- a/e2e/specs/lifecycle.spec.ts +++ b/e2e/specs/lifecycle.spec.ts @@ -120,4 +120,34 @@ test.describe('processor lifecycle', () => { .poll(() => page.evaluate(() => window.harness.counters().webgl.contextsLost)) .toBeGreaterThanOrEqual(before + cycles); }); + + // #142: MediaPipe rejects a failed wasm-loader script with its DOM Event, which has no message. + test('B4: a fileset that fails to load rejects with an Error naming its URL', async ({ app }) => { + const { page } = app; + await page.route('**/missing-wasm/**', (route) => route.fulfill({ status: 404 })); + await app.goto({ backdrop: 'checker', motion: 'static', wasm: '/missing-wasm' }); + await startCamera(page); + + const err = await page.evaluate(() => + window.harness.media.setVideoProcessorEnabled(true).then( + () => null, + (e: unknown) => { + const cause = (e as { cause?: unknown }).cause; + return { + isError: e instanceof Error, + message: String((e as Error).message), + causeIsEvent: cause instanceof Event, + causeTarget: + cause instanceof Event ? (cause.target as Element | null)?.tagName : undefined, + }; + }, + ), + ); + expect(err, 'attaching with a missing fileset resolved').not.toBeNull(); + expect(err?.isError, 'rejection value is an Error').toBe(true); + expect(err?.message).toContain('/missing-wasm/'); + // The browser's original error stays reachable for callers that need more than the message. + expect(err?.causeIsEvent, 'cause is the original Event').toBe(true); + expect(err?.causeTarget).toBe('SCRIPT'); + }); }); diff --git a/src/transformers/BackgroundTransformer.ts b/src/transformers/BackgroundTransformer.ts index 8c8823a..1877a27 100644 --- a/src/transformers/BackgroundTransformer.ts +++ b/src/transformers/BackgroundTransformer.ts @@ -24,6 +24,14 @@ export type BackgroundOptions = { onFrameProcessed?: (stats: FrameProcessingStats) => void; }; +/** + * `cause` is assigned rather than passed to the constructor, because browsers without support for + * the `cause` option ignore it. + */ +function errorWithCause(message: string, cause: unknown) { + return Object.assign(new Error(message), { cause }); +} + export default class BackgroundProcessor extends VideoTransformer { static get isSupported() { return ( @@ -78,6 +86,14 @@ export default class BackgroundProcessor extends VideoTransformer { + // MediaPipe loads its wasm loader through a