From bd9a62e19787dcdcb5ddf9166ab8524d3cb293f6 Mon Sep 17 00:00:00 2001 From: Sarah Rimron-Soutter Date: Wed, 12 Aug 2026 11:49:26 +0100 Subject: [PATCH 1/5] chore(ci): run karma on local headless browsers instead of BrowserStack BrowserStack sessions never start, so the unit, playback and playback-min jobs hang until the 6h timeout. No coverage is lost: Safari is already filtered out of the browser list, so BrowserStack only ever supplied Chrome and a Firefox pinned to version 64. The coverage test type already runs on local browsers and passes green on the same runner. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 123affe22..c108a1129 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,6 @@ jobs: os: [ubuntu-latest] test-type: ${{fromJson(needs.test-type-matrix.outputs.test-type)}} env: - BROWSER_STACK_USERNAME: ${{secrets.BROWSER_STACK_USERNAME}} - BROWSER_STACK_ACCESS_KEY: ${{secrets.BROWSER_STACK_ACCESS_KEY}} CI_TEST_TYPE: ${{matrix.test-type}} runs-on: ${{matrix.os}} steps: From 1d058df3e28270793d3eb66e5e8f019b79c813ab Mon Sep 17 00:00:00 2001 From: Sarah Rimron-Soutter Date: Wed, 12 Aug 2026 12:14:23 +0100 Subject: [PATCH 2/5] chore(ci): prefer headless browsers for local karma detection Without BrowserStack masking it, preferHeadless: false launches non-headless Chrome and Firefox on CI runners. The unit test type is unstable there: Firefox stalls mid-suite in ping-timeout disconnect cycles and Chrome fails three xhr hook tests, while the same build passes fully both locally and under the coverage type. preferHeadless dates to the 2019 generator migration, before headless was the norm. Co-Authored-By: Claude Fable 5 --- scripts/karma.conf.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/karma.conf.js b/scripts/karma.conf.js index 81c2eec88..b8c4fc0db 100644 --- a/scripts/karma.conf.js +++ b/scripts/karma.conf.js @@ -7,7 +7,6 @@ module.exports = function(config) { // for options const options = { coverage: CI_TEST_TYPE === 'coverage' ? true : false, - preferHeadless: false, browsers(aboutToRun) { return aboutToRun.filter(function(launcherName) { return !(/(Safari|Chromium)/).test(launcherName); From cca943d1029142f9cf609b0db8eb99281e3c1fab Mon Sep 17 00:00:00 2001 From: Sarah Rimron-Soutter Date: Wed, 12 Aug 2026 12:39:02 +0100 Subject: [PATCH 3/5] temp: log test starts to locate Firefox CI freeze (revert before merge) Co-Authored-By: Claude Fable 5 --- test/test-helpers.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test-helpers.js b/test/test-helpers.js index 410437133..c89cad319 100644 --- a/test/test-helpers.js +++ b/test/test-helpers.js @@ -9,6 +9,13 @@ import { muxed as muxedSegment } from 'create-test-data!segments'; import {bytesToString, isTypedArray} from '@videojs/vhs-utils/es/byte-helpers'; import {createTimeRanges} from '../src/util/vjs-compat'; +if (window.QUnit) { + window.QUnit.testStart((details) => { + // eslint-disable-next-line no-console + console.log(`TEST: ${details.module} > ${details.name}`); + }); +} + // return an absolute version of a page-relative URL export const absoluteUrl = function(relativeUrl) { return URLToolkit.buildAbsoluteURL(window.location.href, relativeUrl); From 8f1b25c3f7db135cc09e6a795ffcf60f6e330901 Mon Sep 17 00:00:00 2001 From: Sarah Rimron-Soutter Date: Wed, 12 Aug 2026 13:30:39 +0100 Subject: [PATCH 4/5] Revert "temp: log test starts to locate Firefox CI freeze (revert before merge)" This reverts commit cca943d1029142f9cf609b0db8eb99281e3c1fab. --- test/test-helpers.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/test-helpers.js b/test/test-helpers.js index c89cad319..410437133 100644 --- a/test/test-helpers.js +++ b/test/test-helpers.js @@ -9,13 +9,6 @@ import { muxed as muxedSegment } from 'create-test-data!segments'; import {bytesToString, isTypedArray} from '@videojs/vhs-utils/es/byte-helpers'; import {createTimeRanges} from '../src/util/vjs-compat'; -if (window.QUnit) { - window.QUnit.testStart((details) => { - // eslint-disable-next-line no-console - console.log(`TEST: ${details.module} > ${details.name}`); - }); -} - // return an absolute version of a page-relative URL export const absoluteUrl = function(relativeUrl) { return URLToolkit.buildAbsoluteURL(window.location.href, relativeUrl); From 58cb5caa2e4dbca214f03fef36246ef5ae7bbe1a Mon Sep 17 00:00:00 2001 From: Sarah Rimron-Soutter Date: Wed, 12 Aug 2026 13:31:46 +0100 Subject: [PATCH 5/5] chore(ci): raise Firefox per-domain worker cap for the unit suite The unit suite creates a dedicated worker per segment loader per test. Firefox caps dedicated workers at 512 per origin (dom.workers.maxPerDomain) and silently queues creation beyond the cap rather than erroring, so around test 782 a fresh transmuxer worker never starts, its probeTs message is never processed, and the awaited 'appended' event never fires. QUnit has no test timeout, so the suite stalls until karma's no-activity watchdog kills the browser, giving the deterministic disconnect-and-retry failure at ~21 minutes. Chrome has no equivalent cap. The instrumented coverage build passes on Firefox only because it runs slowly enough for GC to reclaim orphaned workers. Raising the pref keeps Firefox in the unit run. Terminating leaked workers in test teardown is the long-term fix. Co-Authored-By: Claude Fable 5 --- scripts/karma.conf.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/karma.conf.js b/scripts/karma.conf.js index b8c4fc0db..04072c830 100644 --- a/scripts/karma.conf.js +++ b/scripts/karma.conf.js @@ -10,6 +10,18 @@ module.exports = function(config) { browsers(aboutToRun) { return aboutToRun.filter(function(launcherName) { return !(/(Safari|Chromium)/).test(launcherName); + }).map(function(launcherName) { + return launcherName === 'FirefoxHeadless' ? 'FirefoxHeadlessHighWorkerLimit' : launcherName; + }); + }, + customLaunchers(defaults) { + return Object.assign(defaults || {}, { + FirefoxHeadlessHighWorkerLimit: { + base: 'FirefoxHeadless', + prefs: { + 'dom.workers.maxPerDomain': 10000 + } + } }); }, files(defaults) {