feat: add local Safari testing via safaridriver#5131
feat: add local Safari testing via safaridriver#5131marcoscaceres wants to merge 21 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a custom Karma launcher to enable running the project’s unit tests locally in Safari via safaridriver, replacing the deprecated/broken third-party Safari launcher dependency.
Changes:
- Introduces
tests/karma.safari.cjs, a minimal W3C WebDriver client/launcher that spawnssafaridriverand navigates to the Karma URL. - Wires the custom launcher into the shared Karma config (
tests/karma.conf.base.cjs). - Removes
karma-safari-launcherfrom dependencies/lockfile.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tests/karma.safari.cjs | New custom Safari launcher using safaridriver + Node http. |
| tests/karma.conf.base.cjs | Replaces karma-safari-launcher plugin with the local Safari launcher. |
| package.json | Drops karma-safari-launcher dependency. |
| pnpm-lock.yaml | Removes karma-safari-launcher entries. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
a2c04c4 to
4f300a5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot i think we should enable this by default to run alongside Chrome and Firefox, even if it’s more expensive to run on macOS in CI. We care that we don’t regress on WebKit/Safari so it’s worth it. |
Done in bd0debf — Safari is now enabled in CI on
|
|
@copilot Can you check and fix ci? |
Fixed in a7d2784. The CI failure had two root causes on cold macOS runners:
The |
|
@copilot Couple of lint errors to fix. See ci. |
Fixed in dab0d02 — two prettier formatting errors in
|
|
Seeing a lot ci fails @copilot. Do your thing, my good every so helpful little digital marvel. |
Fixed in ad409ca — two root causes identified from the Safari CI log:
|
|
@copilot can you review this again and fix any issues you find and any resolve any unresolved comments? |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)
pnpm-lock.yaml:129
- The lockfile no longer lists
lint-stagedunder the root importer’sdevDependencies, butpackage.jsonstill referencesnpx lint-stagedviasimple-git-hooksand includes alint-stagedconfig. This will causepnpm i --frozen-lockfileto omit lint-staged, breaking the configured pre-commit hook. Re-addlint-stagedto the root devDependencies (and regenerate the lockfile), or remove the hook/config if lint-staged is intentionally being dropped.
package.json:56 lint-stagedwas removed fromdevDependencies, but the repo still configuressimple-git-hooks.pre-committo runnpx lint-stagedand includes alint-stagedconfig block later in this same file. With this change, fresh installs won’t have lint-staged available locally, and pre-commit will either fail or unexpectedly download from the network. Either keeplint-stagedas a devDependency or remove/update thesimple-git-hooksandlint-stagedconfiguration to match.
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.2.0",
"karma-mocha-reporter": "^2.2.5",
"loading-indicator": "^2.0.0",
"pluralize": "^8.0.0",
"prettier": "^3.8.2",
"prompt": "^1.3.0",
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
package.json:56
lint-stagedwas removed fromdevDependencies, butsimple-git-hooks.pre-commitstill runsnpx lint-stagedand thelint-stagedconfig remains in this file. This will make pre-commit either fail (no local binary) or rely on network installs vianpx. Re-addlint-stagedtodevDependencies(preferred) or update/remove the hook/config so it doesn't referencelint-staged.
"karma": "^6.4.4",
"karma-chrome-launcher": "^3.2.0",
"karma-firefox-launcher": "^2.1.3",
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.2.0",
"karma-mocha-reporter": "^2.2.5",
"loading-indicator": "^2.0.0",
"pluralize": "^8.0.0",
"prettier": "^3.8.2",
"prompt": "^1.3.0",
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot Can you confirm all your feedback has been addressed? Can you fix anything that was not addressed? |
Done in 039e560. All feedback is now addressed:
|
Replaces the broken karma-safari-launcher (redirect.html hack, broken on modern Safari) with a minimal custom launcher that speaks directly to safaridriver over W3C WebDriver HTTP protocol. No new npm dependencies — uses Node's built-in http module. Setup (one-time): sudo safaridriver --enable Then use as normal: pnpm start --browser Safari Not added to CI (macOS runners cost ~10× more). Closes #2894 for local use.
- Add error/exit handlers on the safaridriver child process so missing or disabled safaridriver gives a clear error message - Reject webdriver() on HTTP 4xx/5xx responses instead of silently resolving with error content - Add 10s timeout to webdriver() requests to prevent hangs - Extract cleanup() helper and call it on both failure and kill paths to prevent orphaned safaridriver processes and session leaks Not implementing poll-for-readiness (comment 2) — the fixed 500ms wait is sufficient for a local dev tool and polling adds complexity.
If safaridriver emitted an 'error' event AND the subsequent webdriver()
call then also failed (connection refused), _done('failure') was called
twice — once in the error handler and once in the catch block.
Fix: race the safaridriver error against the startup timeout using a
Promise. If the process errors, reject immediately and return early,
ensuring _done is called exactly once.
If safaridriver exits before the 500ms timeout without emitting 'error' (e.g. port already in use, not enabled), the Promise would resolve and the launcher would fail later with a confusing WebDriver connection error. Use 'once' on exit during the startup window to reject immediately with a clear message.
Safari returns '' for elem.innerText when the element hasn't been fully laid out, causing the highlight worker to receive empty code and set elem.innerHTML to '<code class="hljs"></code>', emptying the pre. textContent is layout-independent and always returns the raw text, making it the correct choice for extracting code to highlight. Also guard parseInfoString() against undefined infoString, which Safari's marked passes for code blocks without a language specifier.
- Allow SAFARIDRIVER_PORT env var override for karma Safari launcher - Notify Karma and clean up on unexpected safaridriver exit - Fix JSDoc type for parseInfoString to accept undefined
Agent-Logs-Url: https://github.com/speced/respec/sessions/8e1b50fe-6001-4593-9c5d-d20618791bcb Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
…I reliability Agent-Logs-Url: https://github.com/speced/respec/sessions/75aecc21-a5d1-4831-a107-5a4f42a262a9 Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
Agent-Logs-Url: https://github.com/speced/respec/sessions/f825ab10-4ee2-427b-9176-637faabea744 Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
…tReady Agent-Logs-Url: https://github.com/speced/respec/sessions/8f5dd784-fefd-442c-970b-ce6991b8fd8c Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
…down Agent-Logs-Url: https://github.com/speced/respec/sessions/4c275e23-6d86-4c63-9268-4b625420bb32 Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
In Safari, postMessage from opaque-origin srcdoc iframes may not be received (ev.source !== iframe.contentWindow). Add a 100ms polling loop that checks doc.respec.ready directly, racing the existing postMessage listener. Also: - Fix ev.source check to accept null/undefined (loose !=) - Wrap window.parent.postMessage in pubsubhub.js try/catch so postMessage failures don't propagate through pub() and disrupt runAll() - Suppress Promise.reject(err) unhandled rejection in iframe template so Safari doesn't cross-frame propagate it as undefined to jasmine Agent-Logs-Url: https://github.com/speced/respec/sessions/e996459b-6f02-4d6b-9ade-d9003140e5a4 Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
Agent-Logs-Url: https://github.com/speced/respec/sessions/65cc375e-3e6f-43b8-9cf6-3e663ae1544c Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
…ch errors Agent-Logs-Url: https://github.com/speced/respec/sessions/da22e939-2ed4-4370-9fc5-2450af0e5876 Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
Instead of falling back to targetOrigin '*' (which leaks respecConfig data to any embedding parent), return early when parent.location.origin throws SecurityError. The polling fallback in SpecHelper.js handles test completion in Safari. Also guard ev.data access in msgHandler to prevent crashes from non-object message payloads.
Throw early if env var is set to a non-numeric value instead of silently passing NaN as the port argument to safaridriver.
…igin iframes; add port range validation Agent-Logs-Url: https://github.com/speced/respec/sessions/7457d32d-c547-4cd4-94fe-bab03a6343da Co-authored-by: marcoscaceres <870154+marcoscaceres@users.noreply.github.com>
…e for heading (#5207) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
f6388e8 to
d5a21e6
Compare
Enables
pnpm start --browser Safarifor local testing and adds Safari to CI.Problem
karma-safari-launcher@1.0.0(the published package) uses aredirect.htmlhack that modern Safari treats as a file download rather than opening it.@onslap/karma-safari-launcher(the replacement) depends onwd@1.xwhich requires a native build that doesn't compile on Node 24+.Solution
A minimal custom karma launcher (
tests/karma.safari.cjs) that talks directly tosafaridriverover W3C WebDriver HTTP protocol using Node's built-inhttpmodule. No new npm dependencies.Also fixes Safari-specific issues discovered during testing:
postMessagefrom srcdoc iframes: skip whenparent.location.originthrows SecurityError (Safari opaque-origin restriction) instead of falling back totargetOrigin: "*"ev.dataguard in message handler to prevent crashes from non-object payloadswaitReadyfor Safari whereev.source !== iframe.contentWindowtextContentinstead ofinnerTextfor highlight (Safari returns empty for closed<details>)parseInfoStringnull guard for Safari's marked tokenizerSetup (one-time per machine)
CI
Safari runs on
macos-latestin both PR and push workflows. macOS runners cost more than Linux but catching Safari-specific regressions is worth it.Closes #2894