From 138adc2522d11cb2760e903a7fddfc2669dd07e6 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 3 Sep 2025 11:41:37 +0200 Subject: [PATCH 1/2] print browser version --- package.json | 8 +-- tests/run.mjs | 163 -------------------------------------------------- 2 files changed, 4 insertions(+), 167 deletions(-) delete mode 100644 tests/run.mjs diff --git a/package.json b/package.json index 7ce95718..e7916e1d 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,10 @@ "lint:check": "eslint **/*.{js,mjs,jsx,ts,tsx}", "pretty:check": "prettier --check ./", "format:check": "npm run pretty:check && npm run lint:check", - "test:chrome": "node tests/run.mjs --browser chrome", - "test:firefox": "node tests/run.mjs --browser firefox", - "test:safari": "node tests/run.mjs --browser safari", - "test:edge": "node tests/run.mjs --browser edge", + "test:chrome": "node tests/run-browser.mjs --browser chrome", + "test:firefox": "node tests/run-browser.mjs --browser firefox", + "test:safari": "node tests/run-browser.mjs --browser safari", + "test:edge": "node tests/run-browser.mjs --browser edge", "test:shell": "npm run test:v8 && npm run test:jsc && npm run test:spidermonkey", "test:v8": "node tests/run-shell.mjs --shell v8", "test:jsc": "node tests/run-shell.mjs --shell jsc", diff --git a/tests/run.mjs b/tests/run.mjs deleted file mode 100644 index 0b9f5959..00000000 --- a/tests/run.mjs +++ /dev/null @@ -1,163 +0,0 @@ -#! /usr/bin/env node -/* eslint-disable-next-line no-unused-vars */ -import serve from "./server.mjs"; -import { Builder, Capabilities } from "selenium-webdriver"; -import commandLineArgs from "command-line-args"; - -import {logInfo, logError, printHelp, runTest} from "./helper.mjs"; - -const optionDefinitions = [ - { name: "browser", type: String, description: "Set the browser to test, choices are [safari, firefox, chrome, edge]. By default the $BROWSER env variable is used." }, - { name: "port", type: Number, defaultValue: 8010, description: "Set the test-server port, The default value is 8010." }, - { name: "help", alias: "h", description: "Print this help text." }, -]; - - -const options = commandLineArgs(optionDefinitions); - -if ("help" in options) - printHelp(optionDefinitions); - -const BROWSER = options?.browser; -if (!BROWSER) - printHelp("No browser specified, use $BROWSER or --browser", optionDefinitions); - -let capabilities; -switch (BROWSER) { - case "safari": - capabilities = Capabilities.safari(); - break; - - case "firefox": { - capabilities = Capabilities.firefox(); - break; - } - case "chrome": { - capabilities = Capabilities.chrome(); - break; - } - case "edge": { - capabilities = Capabilities.edge(); - break; - } - default: { - printHelp(`Invalid browser "${BROWSER}", choices are: "safari", "firefox", "chrome", "edge"`); - } -} - -process.on("unhandledRejection", (err) => { - logError(err); - process.exit(1); -}); -process.once("uncaughtException", (err) => { - logError(err); - process.exit(1); -}); - -const PORT = options.port; -const server = await serve(PORT); - -async function runTests() { - let success = true; - try { - success &&= await runEnd2EndTest("Run Single Suite", { test: "proxy-mobx" }); - success &&= await runEnd2EndTest("Run Tag No Prefetch", { tag: "proxy", prefetchResources: "false" }); - success &&= await runEnd2EndTest("Run Disabled Suite", { tag: "disabled" }); - success &&= await runEnd2EndTest("Run Default Suite"); - } finally { - server.close(); - } - if (!success) - process.exit(1); -} - -async function runEnd2EndTest(name, params) { - return runTest(name, () => testEnd2End(params)); -} - -async function testEnd2End(params) { - const driver = await new Builder().withCapabilities(capabilities).build(); - const urlParams = Object.assign({ - worstCaseCount: 2, - iterationCount: 3 - }, params); - let results; - try { - const url = new URL(`http://localhost:${PORT}/index.html`); - url.search = new URLSearchParams(urlParams).toString(); - logInfo(`JetStream PREPARE ${url}`); - await driver.get(url.toString()); - await driver.executeAsyncScript((callback) => { - // callback() is explicitly called without the default event - // as argument to avoid serialization issues with chromedriver. - globalThis.addEventListener("JetStreamReady", () => callback()); - // We might not get a chance to install the on-ready listener, thus - // we also check if the runner is ready synchronously. - if (globalThis?.JetStream?.isReady) - callback(); - }); - results = await benchmarkResults(driver); - // FIXME: validate results; - } catch(e) { - throw e; - } finally { - driver.quit(); - } -} - -async function benchmarkResults(driver) { - logInfo("JetStream START"); - await driver.manage().setTimeouts({ script: 2 * 60_000 }); - await driver.executeAsyncScript((callback) => { - globalThis.JetStream.start(); - callback(); - }); - - await new Promise((resolve, reject) => pollResultsUntilDone(driver, resolve, reject)); - const resultsJSON = await driver.executeScript(() => { - return globalThis.JetStream.resultsJSON(); - }); - return JSON.parse(resultsJSON); -} - -class JetStreamTestError extends Error { - constructor(errors) { - super(`Tests failed: ${errors.map(e => e.stack).join(", ")}`); - this.errors = errors; - } - -} - -const UPDATE_INTERVAL = 250; -async function pollResultsUntilDone(driver, resolve, reject) { - const previousResults = new Set(); - const intervalId = setInterval(async function logResult() { - const {done, errors, resultsJSON} = await driver.executeScript(() => { - return { - done: globalThis.JetStream.isDone, - errors: globalThis.JetStream.errors, - resultsJSON: JSON.stringify(globalThis.JetStream.resultsObject("simple")), - }; - }); - if (errors.length) { - clearInterval(intervalId); - reject(new JetStreamTestError(errors)); - } - logIncrementalResult(previousResults, JSON.parse(resultsJSON)); - if (done) { - clearInterval(intervalId); - resolve(); - } - }, UPDATE_INTERVAL) -} - -function logIncrementalResult(previousResults, benchmarkResults) { - for (const [testName, testResults] of Object.entries(benchmarkResults)) { - if (previousResults.has(testName)) - continue; - console.log(testName, testResults); - previousResults.add(testName); - } -} - -setImmediate(runTests); From 7edfd8f3373a40b1997f07e7a64a8ec714a16534 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 3 Sep 2025 11:46:01 +0200 Subject: [PATCH 2/2] add new file --- tests/run-browser.mjs | 165 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 tests/run-browser.mjs diff --git a/tests/run-browser.mjs b/tests/run-browser.mjs new file mode 100644 index 00000000..f1e7eb6a --- /dev/null +++ b/tests/run-browser.mjs @@ -0,0 +1,165 @@ +#! /usr/bin/env node +/* eslint-disable-next-line no-unused-vars */ +import serve from "./server.mjs"; +import { Builder, Capabilities } from "selenium-webdriver"; +import commandLineArgs from "command-line-args"; + +import {logInfo, logError, printHelp, runTest} from "./helper.mjs"; + +const optionDefinitions = [ + { name: "browser", type: String, description: "Set the browser to test, choices are [safari, firefox, chrome, edge]. By default the $BROWSER env variable is used." }, + { name: "port", type: Number, defaultValue: 8010, description: "Set the test-server port, The default value is 8010." }, + { name: "help", alias: "h", description: "Print this help text." }, +]; + + +const options = commandLineArgs(optionDefinitions); + +if ("help" in options) + printHelp(optionDefinitions); + +const BROWSER = options?.browser; +if (!BROWSER) + printHelp("No browser specified, use $BROWSER or --browser", optionDefinitions); + +let capabilities; +switch (BROWSER) { + case "safari": + capabilities = Capabilities.safari(); + break; + + case "firefox": { + capabilities = Capabilities.firefox(); + break; + } + case "chrome": { + capabilities = Capabilities.chrome(); + break; + } + case "edge": { + capabilities = Capabilities.edge(); + break; + } + default: { + printHelp(`Invalid browser "${BROWSER}", choices are: "safari", "firefox", "chrome", "edge"`); + } +} + +process.on("unhandledRejection", (err) => { + logError(err); + process.exit(1); +}); +process.once("uncaughtException", (err) => { + logError(err); + process.exit(1); +}); + +const PORT = options.port; +const server = await serve(PORT); + +async function runTests() { + let success = true; + try { + success &&= await runEnd2EndTest("Run Single Suite", { test: "proxy-mobx" }); + success &&= await runEnd2EndTest("Run Tag No Prefetch", { tag: "proxy", prefetchResources: "false" }); + success &&= await runEnd2EndTest("Run Disabled Suite", { tag: "disabled" }); + success &&= await runEnd2EndTest("Run Default Suite"); + } finally { + server.close(); + } + if (!success) + process.exit(1); +} + +async function runEnd2EndTest(name, params) { + return runTest(name, () => testEnd2End(params)); +} + +async function testEnd2End(params) { + const driver = await new Builder().withCapabilities(capabilities).build(); + const driverCapabilities = await driver.getCapabilities(); + logInfo(`Browser: ${driverCapabilities.getBrowserName()} ${driverCapabilities.getBrowserVersion()}`); + const urlParams = Object.assign({ + worstCaseCount: 2, + iterationCount: 3 + }, params); + let results; + try { + const url = new URL(`http://localhost:${PORT}/index.html`); + url.search = new URLSearchParams(urlParams).toString(); + logInfo(`JetStream PREPARE ${url}`); + await driver.get(url.toString()); + await driver.executeAsyncScript((callback) => { + // callback() is explicitly called without the default event + // as argument to avoid serialization issues with chromedriver. + globalThis.addEventListener("JetStreamReady", () => callback()); + // We might not get a chance to install the on-ready listener, thus + // we also check if the runner is ready synchronously. + if (globalThis?.JetStream?.isReady) + callback(); + }); + results = await benchmarkResults(driver); + // FIXME: validate results; + } catch(e) { + throw e; + } finally { + driver.quit(); + } +} + +async function benchmarkResults(driver) { + logInfo("JetStream START"); + await driver.manage().setTimeouts({ script: 2 * 60_000 }); + await driver.executeAsyncScript((callback) => { + globalThis.JetStream.start(); + callback(); + }); + + await new Promise((resolve, reject) => pollResultsUntilDone(driver, resolve, reject)); + const resultsJSON = await driver.executeScript(() => { + return globalThis.JetStream.resultsJSON(); + }); + return JSON.parse(resultsJSON); +} + +class JetStreamTestError extends Error { + constructor(errors) { + super(`Tests failed: ${errors.map(e => e.stack).join(", ")}`); + this.errors = errors; + } + +} + +const UPDATE_INTERVAL = 250; +async function pollResultsUntilDone(driver, resolve, reject) { + const previousResults = new Set(); + const intervalId = setInterval(async function logResult() { + const {done, errors, resultsJSON} = await driver.executeScript(() => { + return { + done: globalThis.JetStream.isDone, + errors: globalThis.JetStream.errors, + resultsJSON: JSON.stringify(globalThis.JetStream.resultsObject("simple")), + }; + }); + if (errors.length) { + clearInterval(intervalId); + reject(new JetStreamTestError(errors)); + } + logIncrementalResult(previousResults, JSON.parse(resultsJSON)); + if (done) { + clearInterval(intervalId); + resolve(); + } + }, UPDATE_INTERVAL) +} + +function logIncrementalResult(previousResults, benchmarkResults) { + for (const [testName, testResults] of Object.entries(benchmarkResults)) { + if (previousResults.has(testName)) + continue; + console.log(testName, testResults); + previousResults.add(testName); + } +} + +setImmediate(runTests);