Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions test/e2e/lib/framework/createTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,55 @@ import type { Extension } from './createExtension'
import type { Worker } from './createWorker'
import { isBrowserStack } from './environment'

/**
* Init script applied to every WebKit context to work around two Playwright-specific
* quirks observed on the bundled WebKit (Safari 26). Real Safari users are unaffected;
* these only manifest in Playwright's WebKit fork.
*
* 1. Trusted PointerEvent.timeStamp returns a Cocoa-epoch-derived value (~8e11 ms)
* instead of a DOMHighResTimeStamp. The SDK feeds it into relativeToClocks() and
* trips the "clock looks weird" guard in trackClickActions — every click action is
* silently discarded. We wrap Event.prototype.timeStamp to fall back to
* performance.now() when the raw value is implausibly large (>1 year).
*
* 2. window.cookieStore.set() resolves without error but does not persist. The SDK
* picks the CookieStore strategy, the next poll sees an empty cookie, the session
* expires within ~50ms, and every later event is dropped by the session hook. We
* remove window.cookieStore so the SDK falls back to its document.cookie path
* (which works correctly on Playwright WebKit).
*/
const WEBKIT_PLAYWRIGHT_WORKAROUND = `
(() => {
// event.timeStamp is a DOMHighResTimeStamp (ms since performance.timeOrigin), so
// it should always be well below a year. Anything larger is the Cocoa-epoch leak
// observed on Playwright's bundled WebKit (~8e11 ms, ~25 years).
const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000;
const desc = Object.getOwnPropertyDescriptor(Event.prototype, 'timeStamp');
if (desc && desc.get) {
const origGetter = desc.get;
const cache = new WeakMap();
Object.defineProperty(Event.prototype, 'timeStamp', {
configurable: true,
get() {
const raw = origGetter.call(this);
if (raw < ONE_YEAR_MS) return raw;
if (cache.has(this)) return cache.get(this);
const fallback = performance.now();
cache.set(this, fallback);
return fallback;
},
});
}
try {
Object.defineProperty(window, 'cookieStore', {
value: undefined,
configurable: true,
writable: false,
});
} catch (e) {}
})();
`

export function createTest(title: string) {
return new TestBuilder(title, captureCallerLocation())
}
Expand Down Expand Up @@ -349,6 +398,10 @@ function declareTest(title: string, setupOptions: SetupOptions, factory: SetupFa
addTag('test.browserName', browserName)
addTestOptimizationTags(test.info().project.metadata as BrowserConfiguration)

if (browserName === 'webkit') {
await context.addInitScript(WEBKIT_PLAYWRIGHT_WORKAROUND)
}

const servers = await getTestServers()
const baseUrl = new URL(servers.base.origin)
setupOptions.baseUrlHooks.forEach((hook) => hook(baseUrl, servers, setupOptions))
Expand Down
Loading